用GAPI开发Windows Mobile手机游戏

Pocket PC/Smartphone软件安装包之制作DIY 声明  本文来自《msdn开发精选》杂志2005年第4期“特别策划”栏目,本文版权归杂志编辑部所有,未经许可,禁止转载!作者:RAYMAN  当我们开发完Pocket PC或者Smartphone的软件后,都需要考虑如何制作给最终用户使用的软件安装包。专门用于制作安装包的软件有很多,如Install Shield等。但在许多情况下,我们仅仅需要一些比较简单的安装过程,这时候我们完全可以利用SDK中 阅读详情

声明
  本文来自《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影响、快速旋转、从文件或内存装载位图图像、绘制工具、冲突掩码、界面交叉、线程定时器、位图字体支持以及更多。

03.Linux下目录动态扩容,以分配home的部分空间给root为例 本篇文章将介绍Linux下分配home目录的部分空间给root目录。大致思路:卸载home,解除其磁盘容量占用->扩容root,增加其磁盘空间->重建恢复home操作演示环境为CentOS系统,其他操作系统例如openEuler也可作为参考。01.Linux下目录扩容,以增加root目录的磁盘容量分配为例操作系统版本如下名称版本CentOS7.6+ 阅读详情

相关推荐

Localizing LabVIEW Application to Different Languages

本文探讨了LabVIEW应用程序的多语言本地化实现方案。文章首先分析了LabVIEW对多语言的支持现状,指出其默认仅支持ANSI编码,对Unicode支持有限,且仅适配中英法德日韩六种语言。然后重点介绍了JKISimpleLocalization工具包的使用方法,该工具能导出/导入JSON词典文件,实现界面语言的实时切换。文章还结合实际工程案例,分析了该方案的局限性:部分控件不可翻译、切换速度较慢(33个界面需50秒)、小语种支持不足等。最后提供了完整的应用流程和工具包附件,包括词典文件转换工具和工程案例。

2301_80383260的博客 1408

mfc api 文件重命名_VC与WindowsAPI的关系及MFC用C++类封装API的实质

windows程序,其交互的下一层就是Windows操作系统,所以需要用到诸多的Windows API。其它操作系统的应用程序,就要用到其它操作系统的API进行程序开发。构成应用程序GUI的所有元素都必须调用操作系统函数,以编程方式建立。GUI应用程序除了交互方式增加了图形和鼠标以外,整个程序也是通过消息循环与操作系统交互形成事件驱动的。Windows API是在C还是主要通用语言的年代开发的,很...

weixin_39734074的博客 291

raw_count、tpm、fpkm、rpkm如何选择

转录组测序中常见的数据类型有:raw_count、tpm、fpkm、rpkm。本文进行简单辨析: 一、概念 1 raw_count RNA-seq数据中,raw_count一般是指mapped到基因外显子区域的reads数目。比如说htseq,STAR,或者RSEM等NGS分析流程计算产生的counts值。其中RSEM(RNA-Seq by Expectation-Maximization),考虑到一条read 可能会匹配多个exon位置,故而其产生的为expected_count。 2 TPM

weixin_59289660的博客 1万+

Windows API一日一练(25)CreateSolidBrush FillRect函数-创建黑色的背景色

主目录传送门 https://blog.youkuaiyun.com/zb774095236/article/details/108291755 本次学习的源码模板下载: 1.百度云:https://pan.baidu.com/s/1B0YuPWgSxvLTHk59STzQQQ提取码:1ins 学习目标: 在Win32API-13-14-窗口关闭和窗口显示文字版本上改变一块区域的背景色 学习内容: 一、 项目参考 新增函数解释CreateSolidBrushFillRect 1....

爬行的菜鸟的博客 1740

保姆级教程:用北醒TFmini-i-CAN雷达给PixHawk飞控加装‘眼睛’,实现自动避障与定高

经过多次测试,前向雷达的最佳安装位置是机架前部下方5-10cm处,倾斜角度20-30度。电源方面,建议为CAN设备单独供电,避免电机启动时的电压骤降导致数据异常。北醒TFmini-i-CAN雷达与PixHawk飞控的组合,为DIY爱好者打开了低成本实现智能避障的大门。实际部署时,需确保整个CAN网络的波特率一致(TFmini-i默认为250kbps),每个设备具有唯一ID。雷达的安装角度也直接影响检测效果,建议通过3D打印支架将雷达向前下方倾斜15-30度。当使用多个雷达时,需为每个设备创建独立的参数组。

weixin_30892889的博客 337

浪涌电流测试仪的工作原理

浪涌电流脉冲形成的开始由同步电路 13 确定,该同步电路 13 发送命令以打开和第二开关 10 和 11,向发生器 14 发送控制命令以生成被测半导体器件 5 的控制信号和参考信号整形器 9 的触发脉冲。电流源的功率输出并联连接,以对流经被测半导体样品的电流求和。此外,使用数字控制扩展了测试仪的功能,能够产生各种形状的电流脉冲,例如梯形电流脉冲,以估计被测晶闸管的导通状态扩展时间。由于晶体管3的栅极是连接的,并且平衡电阻4包含在晶体管3的源中,因此通过每个晶体管3的电流脉冲大致相同,并重复参考信号的形状。

Jesse0257的博客 1255

【连载】Windows Mobile 贪吃蛇手机游戏开发 1——开发环境的搭建与测试

原视频教程(可免费下载):http://www.itcast.net/course/detail/5611.本节内容介绍l     项目简介;l     需要安装的软件;l     安装Visual Studio 2005;l     安装ActiveSync;l     安装Windows Mobile 6 SDK;l     开发第一个手机程序;2.项目简介

张远山的专栏 2435

LibTV本地部署:AI漫剧创作完整解决方案与实战指南

如果你正在关注AI漫剧创作这个赛道,可能会发现一个有趣的现象:市面上的AI视频工具要么功能单一,要么云端服务昂贵且存在内容审核风险。而今天要介绍的LibTV本地部署方案,或许能为你打开一扇新的大门。 这个方案的核心是Seedance2.0与豆包AI的本地化集成,它真正解决的是创作者对内容自主权、成本控制和创作效率的三重需求。与红果、抖音等平台提供的标准化AI工具相比,本地部署意味着你可以完全掌控生成过程,不受平台规则限制,更重要的是——所有数据都在本地,不存在内容泄露风险。 1. 这篇文章真正要解决的问题

weixin_28745309的博客 521

windows phone 游戏

本文汇集了一些Windows Phone 7游戏开发相关的教程、源代码和书籍,以帮助开发者在Windows Phone 7平台上创建自己的游戏。一、教程1. Windows Phone 7 快速入门:在Windows Phone 7平台创建游戏本教程使用Silverlight作为游戏开发平台,讲解如何获取绘制/更新行为到你的Silverlight游戏中,并提供了一个完整的小游戏来练...

weixin_30699235的博客 223

FPGA开发中的操作系统

在FPGA开发中,操作系统起着关键的作用,它负责任务管理、资源管理、中断处理和设备驱动程序等功能。在实际的FPGA开发中,可以选择使用现有的操作系统,如MicroC/OS-II、FreeRTOS等,或者根据具体需求自行设计和开发操作系统。在FPGA(可编程逻辑门阵列)开发中,操作系统扮演着关键的角色,为硬件设计师提供了一种有效的方式来管理资源、调度任务并实现系统级功能。本文将介绍FPGA开发中的操作系统概念,并提供一个简单的示例代码来说明如何在FPGA平台上实现一个基本的操作系统。

CqpFsharp的博客 1156

2023年全国职业院校技能大赛网络系统管理赛项模块A:网络构建(样题4)-网络部分解析-附详细代码

2023年全国职业院校技能大赛网络系统管理赛项模块A:网络构建(样题4)-网络部分解析-附详细代码

bfq05234214的博客 441

转载:用GAPI开发Windows Mobile手机游戏

 目前mobile phone 游戏API简称GAPI为手机上的游戏开发者提供了强有力的高效率的编程接口,当然GAPI不仅仅使用在游戏方面,需要高效率图形显示处理的地方都可以使用GAPI。  GAPI是基于动态连接库方式,应用程序直接调用动态库里的函数,一般GAPI库的文件名为GX.dll,目前mobile phone里都提供了gx.dll文件。  一个典型的游戏或者应用程序使用下列GAP

快慢机的地盘 1082

MIMIC数据库简介

一 、MIMIC简介 MIMIC(Medical Information Mart for Intensive Care),是一个大型的公开数据库。该数据库记录了2001年至2019年期间贝斯以色列女狄肯斯医疗中心重症监护病房患者的相关数据,拥有4万多名患者的医疗健康数据和记录。 MIMIC数据库记录了人口统计学信息,如病人的性别、身高、宗教等情况。记录了实验室检验信息,如血常规,肝功能,肾功能等实验室检测数据。记录了患者的用药信息,如高血压患者服用了治疗高血压类药物等。记录了护理人员以及患者的护理级别等。

越努力 越幸运 1万+

国外手游公司老板们如何开发游戏?

http://tech.163.com/17/0306/16/CERV25HL00097U7R.html 网易科技讯3月6日消息 据外媒(VentureBeat)报道,手游开发之路艰辛不易,不过Space Ape Games却是例外,这个工作组已经开始呈现良好势头。 公司CEO John Earner在游戏开发者大会上称,从2015年1月开始到今天,Space Ap

我用CV我自豪。 3062

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

FillRect与FrameRect用法

FillRect  函数功能:该函数用指定的画刷填充矩形,此函数包括矩形的左上边界,但不包括矩形的右下边界。  函数原型:int FillRect(HDC hdc, CONST RECT *lprc, HBRUSH hbr);  参数:  hdc:设备环境句柄。  lprc:指向含有将填充矩形的逻辑坐标的RECT结构的指针。  hbr:用来填充矩形的画刷的句柄。  返回值:如果函数调用成功

zhouqian88423的专栏 8602

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

15种手机游戏引擎和开发工具介绍

工欲善其事,必先利其器。对移动游戏开发者来说,高效实用的开发工具必不可少。近日,英国著名产业杂志《Develop》刊出了一篇文章,作者艾伦·李在文中推荐了15种移动游戏开发工具,从游戏引擎,到音效制作、推广等工具都有涉及。以下为原文主要内容编译。 引擎和移动开发工具包 Marmalade 简介:Marmalade被很多人认为是跨平台制作C++游戏的最佳平台。通过Marmalade SDK,开发者可以在单一的Marmalade项目文件夹中打开Xcode或Visual Studio,将

wangchewen的博客 1万+

Web服务架构及其规范入门

作者:Luis Felipe Cabrera、Christopher Kurt、Don Box    [摘要]本Web服务架构入门阐述了Web服务架构的基础设计原则和Web服务的基础技术。此外还对其功能进行了介绍,并提供了对其进行正式定义的规范链接。本文也是该架构所有规范的参考指南。XML和Infoset    对于所有的消息传递系统来说,选择信息传输单位是非常重要的——简单地说,对消息的构成有个

6503

基于SQL Server CE的移动服务系统开发

声明  本文来自《msdn开发精选》杂志2005年第4期“特别策划”栏目,本文版权归杂志编辑部所有,未经许可,禁止转载!作者:侯月文移动服务系统分析与设计移动服务系统需求分析  移动服务是应用比较广泛的移动解决方案,其核心是利用移动计算和无线通信技术为企业现场服务人员提供全面、便捷、实时的信息服务。我们现在就为某电器制造商设计一套用于售后维修服务的移动解决方案。  该电器制造商目前售后维修服务的基

5923

Windows Mobile 5中的新特性

声明  本文来自《msdn开发精选》杂志2005年第4期“特别策划”栏目,本文版权归杂志编辑部所有,未经许可,禁止转载!作者:马宁  微软最近推出下一代移动设备操作系统Windows Mobile 5。Windows Mobile 5在用户体验方面做了很多改善,但更多的改进还是在应用程序编程接口方面。在这篇文章中,我们会向您介绍Windows Mobile 5在开发方面的一些新特性,其中包括3D图

3409
上一篇: Pocket PC/Smartphone软件安装包之制作DIY
下一篇: Windows Mobile中的Web Service应用
dotnet_editor
博客等级 码龄22年 7粉丝 17原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值