用GAPI開發Windows Mobile手機遊戲

來源:互聯網
上載者:User
目前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);
:eleteObject(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);
:eleteObject(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);
:eleteObject(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影響、快速旋轉、從檔案或記憶體裝載位元影像映像、繪製工具、衝突掩碼、介面交叉、線程定時器、位元影像字型支援以及更多。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.