zoukankan      html  css  js  c++  java
  • 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 而被释放.

  • 相关阅读:
    20165220《网络对抗技术》week1 Exp0 Kali安装
    2018-2019-1 20165220 实验五 通讯协议设计
    20165220 《信息安全系统设计基础》第9周学习总结
    20165204 20165216 20165220 实验四 外设驱动程序设计
    20165220 mybash
    2018-2019-1 20165220 《信息安全系统设计基础》第八周学习总结
    20165220 实验三《并发程序》
    2018-2019-1 20165220 《信息安全系统设计基础》第7周学习总结
    2018-2019-1 20165220 《信息安全系统设计基础》第6周学习总结
    实验二 固件程序设计
  • 原文地址:https://www.cnblogs.com/Jade2009/p/1454689.html
Copyright © 2011-2022 走看看