《msdn开发精选》| Windows/.NET频道

mag.csdn.net/msdn | dotnet.csdn.net

用户操作
[即时聊天] [发私信] [加为好友]
霍泰稳ID:dotnet_editor
127507次访问,排名723,好友0人,关注者0人。
dotnet_editor的文章
原创 46 篇
翻译 0 篇
转载 0 篇
评论 572 篇
最近评论
wuyazhe:太好了。顶!
wzq6511:不错
想傲江湖:微软正准备把Visual FoxPro 9.0整合进Visual Studio 2005中。


Microsoft Visual FoxPro Roadmap
March 2006

(摘自:http://msdn.microsoft.com/vfoxpro/roadmap/)

With the recent release……
VFP 爱好者:VFP是一个非常好的数据库开发软件,我用过五年了,设计了很多的数据库软件。真正学透VFP有几个?对于小型的桌面型数据库而言,VFP是首选。VFP有很多的东西,特别是面向对象的编程思想,对于初学者来说理解掌握VFP编程的思想很重要,从而为转学其它的编程工具打下良好的基础。很多的人说VFP不好用,那是没有掌握其真正的技术。MS公司新一代编程工具Visual Studio 2005里面没有整合VF……
无名:好多人说VFP淘汰不好用什么的,甚至有人说用VFP的都是SB,我要告诉这小撮人,你们才是真正的傻B,不管什么工具是有他存在的理由的,只不过运用的场合不同罢了,你们老他妈的把鸡巴往屁眼里插还怪生不出孩子来,所以这小撮人才是天下最大的傻B
文章分类
收藏
    相册
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 用GAPI开发Windows Mobile手机游戏收藏

    新一篇: Windows Mobile中的Web Service应用 | 旧一篇: Pocket PC/Smartphone软件安装包之制作DIY

    声明
      本文来自《msdn开发精选》杂志2005年第4期“特别策划”栏目,本文版权归杂志编辑部所有,未经许可,禁止转载!

    作者:傅曦

      目前mobile phone 游戏API简称GAPI为手机上的游戏开发者提供了强有力的高效率的编程接口,当然GAPI不仅仅使用在游戏方面,需要高效率图形显示处理的地方都可以使用GAPI。

      GAPI是基于动态连接库方式,应用程序直接调用动态库里的函数,一般GAPI库的文件名为GX.dll,目前mobile phone里都提供了gx.dll文件。

      一个典型的游戏或者应用程序使用下列GAPI函数:

    • OpenDisplay (fullscreenflag)
      打开GAPI显示功能。
    • OpenInput
      打开直接响应硬件键盘输入消息功能
    • GetDisplayProperties
      获得VFB详细结构信息
    • GetDefaultKeys
      获得缺省的键值

    操纵GAPI

      开始一个游戏编写,首先要打开GAPI显示功能,获得控制视频显示缓存的控制权限。可以调用

    GXOpenDisplay(HWND hwnd, DWORD dwFlags)

      hwnd参数是游戏程序的窗口句柄,dwFlags定义了显示模式,宏GX_FULLSCREEN定义全屏模式,就能对设备的全屏区域进行控制。返回值1说明打开成功,0是失败。

      虽然都是使用mobile phone系统但是不同系列的产品可能使用不同的显示设备,那么对于不同的显示设备就可能有不同的显示性能参数,不同的分辨率率,不同的色深,不同的颜色显示能力。当在编写一个为mobile phone 系列运行的游戏程序时不得不考虑这些问题,以使程序能适应在不同的显示环境下达到程序所希望的显示效果。

      如何得到这些相关显示信息?可以调用下面函数:

    GXDLL_API GXDisplayProperties GXGetDisplayProperties ();

      它能得到显示设备的所有相关细节信息,这些都是在开发基于GAPI游戏时需要的。所有信息被返回到GXDisplayProperties结构中,其结构如下:

    struct GXDisplayProperties {
        DWORD cxWidth;
        DWORD cyHeight;
        long cbxPitch;
        long cbyPitch;
        long cBPP;
        DWORD ffFormat;
    };

      这个结构提供了显示设备的信息,也就是当前视频缓存区域的参数指标。

      cxWidth和cyHeight是显示设备的宽和高的像素值,提供了显示设备横、纵能显示的像素个数;cBPP是每个像素点需要的位数,总是等于2的n次方。cbxPitch和cbyPitch提供了相邻两个像素间从内存数据上相差的字节数,cbxPitch表示的是左右两个像素间的差值,当cBPP大于等于8时,cbxPitch表示相差的字节个数,当cBPP小于8时,cbxPitch已经不能真实的反映出相差的字节数,事实上必须自己计算得到相邻的地址:

      比如:

    cBPP = 4;
    Leftpointaddr = pb + (((current_x+1) * cBPP) >> 3)
        + (current_y * cbyPitch)

      cbyPitch表示的上下方向间两个像素的差值,计算时通过加减cbyPitch来的到上下方向的像素点的地址:

    downpointaddr = currentpointaddr + cbyPitch;

      ffFormat参数说明显示设备对色深的处理方式及显示的格式:

      当ffFormat 等于 KfLandscape 说明当前显示方式是横屏方式,即原点(0,0)变成了左下角。

      ffFormat 等于 KfPalette 说明色彩显示是基于调色板方式。

      ffFormat 等于 KfDirect 说明色彩显示是直接映射,不引用调色板。

      ffFormat 等于 KfDirectInverted 说明颜色显示是反转的。

      ffFormat 等于 kfDirect555 、kfDirect565、kfDirect888 说明映射颜色显示时,数字表示红绿蓝所占的位信息。

      计算每一个像素坐标地址方法如下(x,y):

    unsigned char * pb;
    if (cBPP < 8) {
    address = pb + ((x * cBPP) >> 3) + (y * cbyPitch)
    }
    else
    {
    address = pb + (x * cbxPitch) + (y * cbyPitch);
    }

    判断是否是标准显示设备

      可以使用函数GXIsDisplayDRAMBuffer (),返回值为TRUE说明是非标准显示设备,返回值为FLASE说明是标准显示设备。当是非标准显示设备时,需要使用函数GXSetViewport来定义显示屏幕的区域,在标准显示设备上使用GXSetViewport是无效的。

    GXDLL_API int GXSetViewport (
        DWORD dwTop,
        DWORD dwHeight,
        DWORD dwReserved1,
        DWORD dwReserved2)

      dwTop定义了屏幕显示区域的y坐标,dwHeight显示区域的高度,dwReserved1、dwReserved被保留,必须需设置为0。

    开始绘制像素

      现在就可以准备对缓存区进行操作绘制图形,通过GXBeginDraw得到缓存区的首地址:

    void * GXBeginDraw ();

      函数返回值就是需要的首地址,如果是NULL说明显示缓存区得不到。然后就可以进行一些列像素的操作,操作完毕后需要调用GXEndDraw 结束一次操作:

    int GXEndDraw ();

      返回1说明调用成功,0说明错误。

      提交绘制的信息,已使变化的画面生效。当程序失去焦点时必须调用GXSuspend ()挂起所有GAPI的操作,把屏幕控制交给其他程序,当接收到获得焦点信息时,程序必须调用GXResume ()使得程序继续运行GAPI函数。

      当退出程序时,必须释放GAPI资源,可以调用:

    int GXCloseDisplay ();

    GAPI高效贴图

      在开发一些图像处理或游戏时我们可以使用GDI制作出满意的产品,但是开发复杂高速的图形显示或高效率的动态游戏时,往往对GDI的显示效率不高而感到沮丧,虽然可以使用双缓存等技术,但是GDI层接口毕竟效率低,无法满足要求。

      GAPI对显示缓存区的直接操作,使显示效率大大提高,所以在目前mobile phone上当需要处理高速贴图时GAPI就当之无愧了。

      虽然GAPI高效强大,提供了对显示缓存区直接的读写权限,但是基于如此低级的功能函数,在编写一个稍微复杂的程序时,就会花费大量的时间和精力在处理对显示缓存区的操作,因为此缓存区并不像GDI提供的绘制缓存区对图片显示一样操作容易,为了显示一幅bmp图就需要编写好几页的代码,这是非常令人厌倦的事。

      在这里主要介绍一下一个使用GAPI编写的第三方贴图类STGapiBuffer。STGapiBuffer提供了类似于GDI方式的接口操作简单,很容易就构造出了需要显示的缓存内容,最后只要简单的把它们拷贝进显示缓存区,就可以显示出来了。

      只要简单的把STGapiBuffer.h 和 STGapiBuffer.cpp加入到工程里面就可以方面的使用了。

      为了需要绘制一个jpg图片,首先需要把图片加载入此类里,并创建适合STGapiBuffer处理的数据,使用CreateNativeBitmap函数,需要如下操作:

    HBITMAP hBitmap = SHLoadDIBitmap(_T("\\image.bmp"));
    g_pNativeBitmap = g_gapiBuffer.CreateNativeBitmap(hBitmap);
    ::DeleteObject(hBitmap);

      接下去需要为绘制到哪里设置目标对象,可以是另一个STGapiBuffer的缓存区,也可以是显示的显示设备缓存区,调用函数SetBuffer,代码如下:

    g_gapiBuffer.SetBuffer(pDisplayBuffer);

      最好可以使用CSTGapiBuffer::BitBlt来把需要的数据绘制到缓存区里,类似于GDI函数BitBlt,代码如下:

    g_gapiBuffer.BitBlt(0, 0, 100, 100, g_pNativeBitmap);

      这样就把一幅图片显示到了屏幕上。

      CSTGapiBuffer还提供了绘制透明图片的函数,在绘图时经常会遇到这样的情况,使用CSTGapiBuffer::MaskedBlt方便的绘制指定透明色的图案。当然我在用GDI绘图时经常使用CreateMemoryDC创建一个临时内存DC来绘制,CSTGapiBuffer也提供了类似的功能的函数CSTGapiBuffer:: CreateMemoryBuffer

      CSTGapiBuffer类使用示例(部分代码):

    CNativeBitmap* pAsteroidBitmap = NULL;
    CNativeBitmap* pAsteroidMask = NULL;

    CSTGapiBuffer gapiBufferBackground; // 背景
    CSTGapiBuffer gapiBufferMemory;
    CSTGapiBuffer gapiBufferScreen;

    HBITMAP hBackground = ::LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BACKGROUND));
    CNativeBitmap * pBackgroundBitmap =
        gapiBufferMemory.CreateNativeBitmap(hBackground);
    ::DeleteObject(hBackground);

    gapiBufferBackground.CreateMemoryBuffer();
    gapiBufferBackground.BitBlt(0, 20, dwDispWidth, dwDispHeight,
        pBackgroundBitmap);
    delete pBackgroundBitmap;
    pBackgroundBitmap = NULL;

    ////////////////////////////////////

    HBITMAP hAsteroid = ::LoadBitmap(hInst, MAKEINTRESOURCE(IDB_ASTEROID));
    pAsteroidBitmap = gapiBufferMemory.CreateNativeBitmap(hAsteroid);
    ::DeleteObject(hAsteroid);

    HBITMAP hAsteroidMask = ::LoadBitmap(hInst, MAKEINTRESOURCE(IDB_ASTEROID_MASK));
    pAsteroidMask = gapiBufferMemory.CreateNativeBitmap(hAsteroidMask);
    ::DeleteObject(hAsteroidMask);

    /////////////////////////////////////////
    //

    dwTransparentColor = gapiBufferMemory.GetNativeColor(RGB(249, 57, 198));

    // create an offscreen buffer
    gapiBufferMemory.CreateMemoryBuffer();

    gapiBufferMemory.BitBlt(&gapiBufferBackground);

    ///////////////////////////////////////////////////

    //
    gapiBufferMemory.TransparentBltEx(0, nOffset%dwDispHeight,
        50, 60, pAsteroidBitmap, dwTransparentColor);

    gapiBufferMemory.TransparentBltEx(100, (nOffset*2)%dwDispHeight,
        50, 60, pAsteroidBitmap, dwTransparentColor);

    gapiBufferMemory.TransparentBltEx(nOffset%dwDispWidth,
        50, 50, 60, pAsteroidBitmap, dwTransparentColor);

    gapiBufferMemory.TransparentBltEx((nOffset*2)%dwDispWidth,
        150, 50, 60, pAsteroidBitmap, dwTransparentColor);

    gapiBufferMemory.MaskedBlt(dwDispWidth-(nOffset*2)%dwDispWidth,
        (nOffset*8)%dwDispHeight, 50, 60, pAsteroidBitmap, pAsteroidMask);

    gapiBufferMemory.MaskedBlt((nOffset%dwDispWidth*5), (nOffset*3)%
        dwDispHeight, 50, 60, pAsteroidBitmap, pAsteroidMask);

    gapiBufferMemory.MaskedBlt((nOffset*3)%dwDispWidth, (nOffset*4)%
        dwDispHeight, 50, 60, pAsteroidBitmap, pAsteroidMask);

    gapiBufferMemory.MaskedBlt((nOffset%dwDispWidth*2), dwDispHeight-
        (nOffset*5)%dwDispHeight, 50, 60, pAsteroidBitmap, pAsteroidMask);

    RECT rc = { 0, 20, dwDispWidth/3, 10 };
    FillRect(&rc, RGB(255, 0, 0));

    rc.left = dwDispWidth/3;
    rc.right = 2*dwDispWidth/3;
    FillRect(&rc, RGB(0, 255, 0));

    rc.left = 2*dwDispWidth/3;
    rc.right = dwDispWidth;
    FillRect(&rc, RGB(0, 0, 255));

    void* pBuffer = GXBeginDraw();
    gapiBufferScreen.SetBuffer(pBuffer);
    gapiBufferScreen.BitBlt(&gapiBufferMemory);
    GXEndDraw();

    Gapi键盘消息

      使用GXOpenInput()函数获得键盘的控制权,调用GXGetDefaultKeys(GX_NORMALKEYS)函数来获得默认键盘的消息映射。然后在Windows消息处理函数中我们就能收到由GAPI发送过来的键盘消息,当你按下某一个键,程序会收到WM_KEYDOWN,wParam参数包含GAPI映射的这个键的消息,通过与得到的GXKeyList结构中的字段定义来判断当前收到的键是不是定义的功能键。

      GXKeyList结构如下:

    struct GXKeyList {
        short vkUp;
        POINT ptUp;
        Short vkDown;
        POINT ptDown;
        Short vkLeft;
        POINT ptLeft;
        Short vkRight;
        POINT ptRight;
        Short vkA;
        POINT ptA;
        Short vkB;
        POINT ptB;
        Short vkC;
        POINT ptC;
        Short vkStart;
        POINT ptStart;
    };

    游戏的振动感

      在游戏中提供振动效果,是让玩家非常兴奋的事情,可以提升游戏的吸引程度。Mobile Phone SDK 提供了振动效果的API,允许你控制振动,类似与控制声音一样简单。

    获得振动设备属性

      要获知手机是否支持振动,振动的性能,当前的设置情况,可以调用下面的函数:

    int VibrateGetDeviceCaps(VIBRATEDEVICECAPS vdc);

      VIBRATEDEVICECAPS是个枚举类型,结构如下

    typedef enum {
        VDC_AMPLITUDE,
        VDC_FREQUENCY,
        VDC_LAST
    } VIBRATEDEVICECAPS

    • VDC_AMPLITUDE 查询振动设备所能支持的振幅大小
    • VDC_FREQUENCY 查询振动设备所能支持的振动频率大小
    • VDC_LAST 查询振动设备所能支持的振幅大小

      如果函数成功,它将返回数字0到7,数字0说明设备没有提供振动功能,1说明设备具有振动功能,并且可以使用,但是仅仅具有打开关闭震动,无法对振动进行调节,2到7说明了设备提供了不同等级的振动功能,数字越大提供的调节能力越强。当设备具有不同等级振动能力时,我就可以通过VIBRATENOTE结构做详细设置。

      怎么才能开始真正的使用振动功能呢?mobile phone SDK提供Vibrate函数:

    HRESULT Vibrate(
        DWORD cvn,
        const VIBRATENOTE * rgvn,
        BOOL fRepeat,
        DWORD dwTimeout
    );

      它能提供不同振幅,不同频率,并可以调节需要振动时间。cvn参数是第二个参数rgvn数组的维数,rgvn是指向一组VIBRATENOTE结构的指针。

      VIBRATENOTE结构如下:

    typedef struct {
        WORD wDuration;
        BYTE bAmplitude;
        BYTE bFrequency;
    } VIBRATENOTE

      wDuration说明了震动持续的时间,bAmplitude定义了振动的振幅大小,允许设置0-7级,如果等于0xff系统使用缺省值作为参数,bFrequency定义了振动的频率高低,允许设置0-7级,如果0xff系统使用缺省值作为参数。

      当你需要停止当然的振动时,可以调用VibrateStop()函数,返回S_OK说明成功调用,E_FAIL说明调用失败。

      下面是代码示例:

    int caps = -1;
    caps = VibrateGetDeviceCaps(VDC_AMPLITUDE);
    if(caps<=0)
        return FALSE; //振幅返回失败,说明不支持振动功能

    HRESULT hr = Vibrate(0, NULL, TRUE, INFINITE); //设定为无时间限制
    if(hr == E_FAIL)
    {
        MessageBox(NULL,L"E_FAIL",L"",MB_OK);
    }
    else if(hr == E_NOTIMPL)
    {
        MessageBox(NULL,L"E_NOTIMPL",L"",MB_OK);
    }

    Sleep(1000); //振动所花时间

    VibrateStop();

    开始第一手机游戏历程

      在这里使用GAPI模拟一个贪食者游戏,它非常简单。主要注重怎么具体使用GAPI,在使用中怎么对视频缓存区操作演示,并不去美化外表。

      初始化GAPI库,在InitInstance函数里我们对GAPI的显示和输入进行了初始化。

    if (GXOpenDisplay( hWnd, GX_FULLSCREEN) == 0)
        return FALSE;
    gx_displayprop = GXGetDisplayProperties();
    if (gx_displayprop.cBPP != 16)
    {
        // Only dealing with 16 bit color in this code
        GXCloseDisplay();
        MessageBox(hWnd,L"Sorry, only supporting 16bit color",L"Sorry!",     MB_OK);
        return FALSE;
    }
    framebuf = (unsigned short*) malloc(sizeof(short)*gx_displayprop.cxWidth*gx_displayprop.cyHeight);
    if(framebuf==NULL)
        return FALSE;
    ClearScreen(framebuf,0xff,0xff,0xff);
    GXOpenInput();

    // Get default buttons for up/down etc.
    gx_keylist = GXGetDefaultKeys(GX_NORMALKEYS);

      初始化工作完成后,我们就需要对游戏的内容进行必要准备工作。我们首先初始化贪食者对象,我们为它建立蛇头和它的身体。为了使贪食者不停的游动,必须有一个事件触发。在这个我们使用定时器,以100ms的间隔发送消息,这样将得到一秒10帧的效果,这足以满足普通游戏的效果。

      为了对定时器发送过来的消息进行处理,我们调用Run函数

    void Run(HWND hwnd)
    {
        if(1 == JudgeDeath(framebuf))
        {
            KillTimer(hwnd,1);
            RunVibrate(1000);
            MessageBox(hwnd,_T("Snake has died!"),_T("died"),
                MB_OK | MB_ICONINFORMATION);
            SendMessage(hwnd,WM_PAINT,0,0);
            InvalidateRect(hwnd,NULL,TRUE);
        }
        ChangeDirection();
        SortAll();
        RedrawSnake();
    }

      JudgeDeath每次都会判断是否已经死亡(蛇的任何部位有重叠),一旦满足死亡的条件,就取消定时器,以便中止蛇的游动。ChangeDirection判断方向是否发生了改变。在这里我对蛇部位的重叠利用了两个象素是否相同的颜色,如果颜色一直说明重叠发生。

    void Get16Pixel(unsigned short *buffer,int x, int y,int *r, int *g, int *b)
    {
        unsigned short *pixeladd;
        int address = (x * gx_displayprop.cbxPitch>>1)
            + (y * gx_displayprop.cbyPitch>>1);
        pixeladd = (buffer+address);
        if (gx_displayprop.ffFormat & kfDirect565)
        {
            unsigned short PixelCol;
            PixelCol = (*pixeladd);
            *r = (PixelCol & 0xf800) >> 11;
            *g = (PixelCol & 0x07e0) >> 5;
            *b = (PixelCol & 0x001f);
        }
        else//555
        {
            unsigned short PixelCol;
            PixelCol = (*pixeladd);
            *r = (PixelCol & 0x7c00) >> 11;
            *g = (PixelCol & 0x03e0) >> 5;
            *b = (PixelCol & 0x001f);
        }
    }

    第三方开发库介绍

      GapiDraw的设计与DirectDraw非常相似,而且将更加容易使用,极大限度的为掌上设备进行了优化。下面是一些DriectDraw中的一般功能,以及如何在GapiDraw中实现这些功能。

    打开显示设备

      计算机的显示内存是一块包含了图像数据的内存区域。为了直接向这块区域进行写操作,DirectDraw和GapiDraw都需要你创建一个指定的称之为主界面的界面。直接绘制到这个主界面来影响屏幕的可见内容。

      创建主界面的第一步是打开显示器,设置一个显示模式。下面的步骤是使用DirectDraw创建主界面的最少步骤。

    DirectDraw

    LPDIRECTDRAW lpDD;

    HRESULT ddrval;

    //创建主Direct Draw对象
    ddrval = DirectDrawCreate(NULL, &lpDD, NULL);
    if(ddrval != DD_OK)
    {
        return(false);
    }

    //设置合作级别以允许Direct Draw全屏运行
    ddrval = lpDD->SetCooperativeLevel(hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
    if(ddrval != DD_OK)
    {
        lpDD->Release();
        return(false);
    }

    //设置显示模式为320x240x16
    ddrval = lpDD->SetDisplayMode(320, 240, 16);
    if(ddrval != DD_OK)
    {
        lpDD->Release();
        return(false);
    }

      使用GapiDraw是比较容易的。因为界面是对象而不是COM接口,所以根本就不用手工进行释放。下面的GapiDraw例子只使用了一条命令打开显示设备,设置默认显示模式。

    GapiDraw

    CGapiDisplay display;

    HRESULT gdrval;

    //使用标准Pocket PC240x320x16模式打开显示
    gdrval = display.OpenDisplay(hwnd, GDOPENDISPLAY_FULLSCREEN);
    if(gdrval != GD_OK)
    {
        return(false);
    }

    取回主界面和后背缓冲

      使用Direct Draw,你必须手工请求创建一个指定界面的主Direcr Draw对象,主界面主要用于直接在显示器上绘制。在Direct Draw中只有一个界面接口用于双内存界面和显示。这可以简单地解释为过去使用的COM模式中的子类化缺陷。下面的例子创建一个主界面,使用Direct Draw取回它的后背缓冲。

    DirectDraw

    LPDIRECTDRAWSURFACE lpDDSPrimary; // DirectDraw 主界面
    LPDIRECTDRAWSURFACE lpDDSBack; // DirectDraw 后背缓冲

    DDSURFACEDESC ddsd;
    DDSCAPS ddscaps;
    HRESULT ddrval;

    memset(&ddsd, 0, sizeof(ddsd));
    ddsd.dwSize = sizeof(ddsd);
    ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
    ddsd.dwBackBufferCount = 1;

    //创建主界面
    ddrval = lpDD->CreateSurface(&ddsd, &lpDDSPrimary, NULL);
    if(ddrval != DD_OK)
    {
        lpDD->Release();
        return(false);
    }

    // Get the back buffer获得背后缓冲
    ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
    ddrval = lpDDSPrimary->GetAttachedSurface(&ddscaps, &lpDDSBack);
    if(ddrval != DD_OK)
    {
        lpDDSPrimary->Release();
        lpDD->Release();
        return(false);
    }

      相反,使用GapiDraw 事情会更容易。一旦调用了CGapiSurface::OpenDisplay,CGapiDisplay 对象自动地变成主界面。因为CGapiDisplay 是CGapiSurface的子类,所有的位图传输(blit)和绘制操作都已经是可用的了。为了从CGapiDisplay得到后背缓冲,使用如下代码:

    GapiDraw

    CGapiSurface backbuffer; // GapiDraw 后背缓冲

    HRESULT gdrval;
    //得到后背缓冲
    gdrval = display.GetBackBuffer(&backbuffer);
    if(gdrval != GD_OK)
    {
        return(false);
    }

    失败的界面

      DirectDraw的界面通常储存在图像存储器中,可以在任何时候被覆盖(在用户切换程序或者启动另外一个使用GDI的程序的情况下)。这是因为对界面的每个操作在任何时候都有可能失败,简单地说是因为界面数据被覆盖。因此所有使用Direct Draw的操作必须每次检查界面是否失败,然后从失败处手工恢复和重新创建界面。下面的例子说明了这一点。

    DirectDraw

    ddrval = lpDDSBack->Blt(&rcRectDest, lpDDSMySurf, &rcRectSrc, DDBLT_WAIT, NULL);
    if(ddrval == DDERR_SURFACELOST)
    {
        //界面被覆盖,现在你必须手工恢复和重新创建所有的界面
    }

    ddrval = lpDDSPrimary->Flip(NULL, DDFLIP_WAIT);
    if(ddrval == DDERR_SURFACELOST)
    {
        //界面被覆盖,现在你必须手工恢复和重新创建所有的界面
    }

      Pocket PC不使用图像存储器,所有界面数据被储存在RAM物理内存,只有调用CGapiDisplay::Flip时才被复制到显示区域。如果Pocket PC设备访问了显示区域的缓冲,那么Pocket PC可能会移动它的后背缓冲的位置。到目前为止,还没有设备被告之可以做到这点,但是它始终是最好的设计。可以使用下面的代码捕获在GapiDraw中丢失的后背缓冲。

    GapiDraw

    //普通操作中的界面不能丢失
    gdrval = backbuffer.Blt(&rcRectDest, &mysurf, &rcRectSrc, 0, NULL);

    gdrval = display.Flip();
    if(gdrval == GDERR_BACKBUFFERLOST)
    {
        //显示缓冲被移动,刚好获得一个更新的后背缓冲
        display.GetBackBuffer(&backbuffer);
    }

    结论

      上面提及的是GapiDraw和Direct Draw之间的主要不同。其它的特性,象blit、颜色值、矩形坐标等都是有差异的。GapDraw也包含一个巨大的扩展特性,这些特性在Direct Draw不可用,象高级的blit影响、快速旋转、从文件或内存装载位图图像、绘制工具、冲突掩码、界面交叉、线程定时器、位图字体支持以及更多。

    发表于 @ 2005年08月19日 17:08:00|评论(loading...)|编辑

    新一篇: Windows Mobile中的Web Service应用 | 旧一篇: Pocket PC/Smartphone软件安装包之制作DIY

    评论

    #wuyazhe 发表于2007-09-07 01:19:15  IP: 218.19.168.*
    太好了。顶!
    发表评论  


    当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
    Csdn Blog version 3.1a
    Copyright © dotnet_editor