DirectDraw6的初級程式設計應用 —— 配合例項講解,讓你迅速入門 (轉)

worldblog發表於2007-12-04
DirectDraw6的初級程式設計應用 —— 配合例項講解,讓你迅速入門 (轉)[@more@]

DirectDraw6的初級應用
by Gamster H. S

目錄:
1. VC的環境設定.
2. 淺談COM.
3. DirectDraw簡述.
4. DirectDraw的初級程式設計應用.


第一篇:VC的環境設定.
在進行任何有關的程式設計之前,你得先設定VC的環境變數值.主要是方便以後的專案設計.
我使用的是VC5和DirectX6,這是我的設定:

1. 選擇Options.
2. 選擇Directories Tab.
3. 在Show directories for框中選擇include files
4. 在Directories框中選擇一項新項,鍵入C:mssdkinclude,此目錄將含有所有有關的頭,
如:ddraw.h, dsound.h, ...
5. 在Show directories for框中選擇Library files
6. 在Directories框中選擇一項新項,鍵入C:mssdklib,此目錄將含有所有有關的庫檔案,
如:ddraw.lib, dsound.lib, dxguid.lib, ...
7. 選擇OK

當你編譯建立的程式時,以下是基本步驟是:

1. 將整個ddex1(c:mssdkmultimediaddrawddrawsrcddex1)目錄copy到你任為合適的目
錄中(如d:mydd,全稱為d:myddddex1).
2. 在VC5的選單項Files中選擇New.
3. 選擇 Application.
4. 鍵入合適的Location(如d:mydd).
5. 鍵入合適的Project Name(在此為ddex1).
6. 選擇選單項Project->Add to Project->Files,加入*.cpp檔案,*.rc檔案.在ddex1專案中,
你要加入ddex1.cpp和ddex1.rc檔案.
7. 選擇選單項Project->Settings,在Link中的/Library Modules中加入ddraw.lib
dxguid.lib.
8. Build and run.
9. Any prblems? You know where to find me!

第二篇:淺談COM.
我不大清楚,但沒幾個人混得清楚的.他們要麼知道點皮毛,要麼還在階段鍛練著.別人問
起,只說DirectX3,5,6運用許多COM技術.

第三篇:DirectDraw簡述.
DirectX大大提高在Win環境下的運用.DirectDraw提高各種顯示硬體的工作.

第四篇:DirectDraw的初級程式設計應用.
本文將簡述ddex1至ddex5中各種DirectDraw 的運用(in detail),至於更高的設計運用,望各位
自己慢慢從其他人手中學之.

1. DirectDraw例子中的全域性變數.


// Global data

LPDIRECTDRAW4  g_pDD = NULL;  // DirectDraw object
LPDIRECTDRAWSURFACE4  g_pDDSPrimary = NULL;// DirectDraw primary surface
LPDIRECTDRAWSURFACE4  g_pDDSBack = NULL;  // DirectDraw back surface
LPDIRECTDRAWSURFACE4  g_pDDSOne = NULL;  // Offscreen surface 1
LPDIRECTDRAWPALETTE  g_pDDPal = NULL;  // The primary surface palette
BOOL  g_bActive = FALSE;  // Is application active?

以上全域性變數分別為:
1)IDirectDraw4的指標.用它建立其它幾個全域性變數.
2)IDirectDrawSue4的物件指標,在此是3個:
  g_pDDSPrimary是顯示緩衝塊,用於顯示;
  g_pDDSBack是幕後顯示緩衝塊,從後屏緩衝區截圖後拼在此緩衝塊上,隨後翻成顯示緩衝
塊,原有顯示緩衝塊翻成幕後顯示緩衝塊;
  g_pDDSOne是後屏緩衝區,用於不同圖形資料.
3)IDirectDrawPalette的物件指標,用於儲存特定BITMAP的調色盤.用在Color manipulation.
4)另一變數和DirectDraw無任何關係.只是確認是否處於執行狀態.
以上的編碼含有說明,所以對任何人都易理解.Any problems? You know where to find me!


2.初始化所有全程變數
這些原碼我抄自ddex5.cpp中的static HRESULT InitApp(HINSTANCE hInstance, int nCmdShow)中:

1)定義幾個區域性變數:
  DDSURFACEDESC2  ddsd;  // the structure contain information about a particluar DirectDrawSurface.
  DDSCAPS2  ddscaps;  // this is important for further step
  HRESULT  hRet;   // error output
  LPDIRECTDRAW  pDD;  // IDirectDraw Object.

2)組建各項物件:

  ///////////////////////////////////////////////////////////////////////////
  // Create the main DirectDraw object
  ///////////////////////////////////////////////////////////////////////////
  hRet = DirectDrawCreate(NULL, &pDD, NULL);
  if (hRet != DD_OK)
  return InitFail(hWnd, hRet, "DirectDrawCreate FAILED");

先建立一個IDirectDraw Object,hRet裝有DirectDrawCreate()的返回值,這一招非常有用,所有的DirectX
函式都返回一定的數值,DirectDraw的函式返回DD_OK表示函式成功.DDERR_***的返回值表示函式
呼叫失敗(或其他原因).檢查函式返回值,以備以後布驟不會出錯.我將推遲解釋InitFail()函式.

  // Fetch DirectDraw4 interface
  hRet = pDD->QueryInterface(IID_IDirectDraw4, (LPVOID *) & g_pDD);
  if (hRet != DD_OK)
  return InitFail(hWnd, hRet, "QueryInterface FAILED");

我們用COM中的QueryInterface申請一個IDirectDraw4物件,不同於DIRECTX5,DirectDraw增加了IDirectDraw4
介面,同時新增了幾個更有用的函式(METHODS).你可以從DirectX的幫助檔案中查到.雖是英文,
但好好啃一啃還是有收穫的.

  // Get exclusive mode
  hRet = g_pDD->SetCooperativeLevel(hWnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
  if (hRet != DD_OK)
  return InitFail(hWnd, hRet, "SetCooperativeLevel FAILED");

我們接著設定顯示方式,你可設定全屏,或視窗型,或MODEX,從DDEX1到DDEX5的程式中使用的顯
示方式都是DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN,全屏!你可以從DirectX的幫助檔案中查到有關
SetCooperativeLevel()的註解.雖是英文,但好好啃一啃還是有收穫的.

  // Set the vo mode to 640x480x8
  hRet = g_pDD->SetDisplayMode(640, 480, 8, 0, 0);
  if (hRet != DD_OK)
  return InitFail(hWnd, hRet, "SetDisplayMode FAILED");

我們接著設定螢幕大小,色素和重新整理速度.你可以從DirectX的幫助檔案中查到有關SetDisplayMode()的
註解.雖是英文,但好好啃一啃還是有收穫的.全屏下你所設顯示可為320X200, 640X480, 800X600,
1024X768, 1280X1024, 1600X1280等等.色素從8, 16, 24, 32BITs.

  // Create the primary surface with 1 back buffer
  ZeroMemory(&ddsd, sizeof(ddsd));
  ddsd.dwSize = sizeof(ddsd);
  ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
  ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |
  DDSCAPS_FL|
  DDSCAPS_COMPLEX;
  ddsd.dwBackBufferCount = 1;
  hRet = g_pDD->CreateSurface(&ddsd, &g_pDDSPrimary, NULL);
  if (hRet != DD_OK)
  return InitFail(hWnd, hRet, "CreateSurface FAILED");

  // Get a pointer to the back buffer
  ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
  hRet = g_pDDSPrimary->GetAttachedSurface(&ddscaps, &g_pDDSBack);
  if (hRet != DD_OK)
  return InitFail(hWnd, hRet, "GetAttachedSurface FAILED");

建立一顯示緩衝塊附帶一塊幕後顯示緩衝塊.首先你初始化DDSURFACEDESC2的物件.如下:

  ZeroMemory(&ddsd, sizeof(ddsd));
  ddsd.dwSize = sizeof(ddsd); // you will fail without this!!

我觀查到的現象是,每個DirectDraw結構含有dwSize成員,一定要初始化這個成員如下:
  ddStructObj.dwSize = sizeof(DDSTRUCT); // you will fail without this!!

以下一段編碼

  ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
  ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |
  DDSCAPS_FLIP |
  DDSCAPS_COMPLEX;

表示所建DirectDrawSurface是Primary Surface(顯示緩衝塊),Flipable(前後翻轉),有Back Buffer
(幕後顯示緩衝塊)連線到Primary Surface(attached to).

  ddsd.dwBackBufferCount = 1;

表示Back Buffer(幕後顯示緩衝塊)只有一塊.

  hRet = g_pDD->CreateSurface(&ddsd, &g_pDDSPrimary, NULL);
  if (hRet != DD_OK)
  return InitFail(hWnd, hRet, "CreateSurface FAILED");

  // Get a pointer to the back buffer
  ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
  hRet = g_pDDSPrimary->GetAttachedSurface(&ddscaps, &g_pDDSBack);
  if (hRet != DD_OK)
  return InitFail(hWnd, hRet, "GetAttachedSurface FAILED");

這一段編碼建立Primary Surface,再建立Back Buffer.至此我們的初始工程基本結束!

3.出錯處理.
現在介紹初始化過程中錯誤的處理.
  return InitFail(hWnd, hRet, "...... FAILED");
這一語段的運用是,當某一步初始化過程出錯,整個程式退出,並顯示一個對話方塊.其定義如下:


// Name: InitFail()
// Desc: This function is called if an initialization function fails

HRESULT InitFail(HWND hWnd, HRESULT hRet, LPCTSTR szError,...)
{
  char  szBuff[128];
  va_list  vl;

  va_start(vl, szError);
  vsprintf(szBuff, szError, vl);
  ReleaseAllObjects();
  MessageBox(hWnd, szBuff, TITLE, MB_OK);
  DestroyWindow(hWnd);
  va_end(vl);
  return hRet;
}

你要增寫一個RelaeseAllObject()函式,此函式將釋放所有的DDRAW物件.有一點值得申明.如果對
象早已被釋放,程式不知,試圖釋放不存在的物件將有嚴重後果,小心!!!!


// Name: ReleaseAllObjects()
// Desc: Finished with all objects we use; release them

static void ReleaseAllObjects(void)
{
  if (g_pDD != NULL)
  {
  if (g_pDDSPrimary != NULL)
  {
  g_pDDSPrimary->Release();
  g_pDDSPrimary = NULL;
  }
  if (g_pDDSOne != NULL)
  {
  g_pDDSOne->Release();
  g_pDDSOne = NULL;
  }
  if (g_pDDPal != NULL)
  {
  g_pDDPal->Release();
  g_pDDPal = NULL;
  }
  g_pDD->Release();
  g_pDD = NULL;
  }
}

這裡,程式先釋放IDirectDraw4下屬的物件,再釋放IDirectDraw4的物件.釋放前,程式先確認各對
象是否存在,如物件還駐留於內村中,釋放物件,並賦值物件指標NULL.

4.如何恢復暫時丟失的物件值.
在你按ALT+TAB鍵後,應用程式相互切換,當你從其他應用程式轉換到DirectDraw有關應用程式時,原
有的物件緩衝區將在切換過程中被I/O釋放.稱為丟失(LOST).要重新建立.只有IDirectDraw4對
象屬下的IDirectDrawSuface4物件有丟失的可能,所以IDirectDrawSurface4提供了Restore()程式.使用方式
如下:


// Name: RestoreAll()
// Desc: Restore all lost objects

HRESULT
RestoreAll(void)
{
  HRESULT  hRet;

  hRet = g_pDDSPrimary->Restore();
  if (hRet == DD_OK)
  {
  hRet = g_pDDSOne->Restore();
  if (hRet == DD_OK)
  {
      // 重新將Bitmap讀入.DDReloaitmap()
  }
  }

  return hRet;
}

一般,DirectDrawSuface4物件的丟失將沖刷物件所含的Bitmap.故要使用DDReLoadBitmap()函式重新從
Bitmap檔案中讀入記憶體.此函式收藏於DDUTIL.CPP檔案中.並不屬於DirectX的一部分.下面,就讓我
介紹如何使用DDUTIL.CPP中的函式.

5.使用DirectDraw Utilities!!
為了方便開發者,初學者對DirectX的困惑,MS DirectX設計組專門寫了一個DDUTIL.CPP檔案來幫助大
家.它提供了方便的函式來進行讀取Bitmap並賦值於IDirectDrawSurface4物件,從Bitmap中讀取調色盤
資訊,設定透明色等方程.定義如下:

/*==========================================================================
 *  File:  ddutil.cpp
 *  Content:  Routines for loading bitmap and palettes from res
 ***************************************************************************/

#ifdef __cplusplus
extern "C" {  /* Assume C declarations for C++ */
#endif /* __cplusplus */

extern IDirectDrawPalette  *DDLoadPalette(IDirectDraw4 *pdd, LPCSTR szBitmap);
extern IDirectDrawSurface4 *DDLoadBitmap(IDirectDraw4 *pdd, LPCSTR szBitmap, int dx, int dy);
extern HRESULT  DDReLoadBitmap(IDirectDrawSurface4 *pdds, LPCSTR szBitmap);
extern HRESULT  DDCopyBitmap(IDirectDrawSurface4 *pdds, HBITMAP hbm, int x, int y, int dx, int dy);
extern D  DDColorMatch(IDirectDrawSurface4 *pdds, COLORREF rgb);
extern HRESULT  DDSetColorKey(IDirectDrawSurface4 *pdds, COLORREF rgb);

#ifdef __cplusplus
}
#endif /* __cplusplus */


-> DDLoadPalette(IDirectDraw4 *pdd, LPCSTR szBitmap):
從Bitmap檔案中讀出調色盤資訊,並將此資訊交送於IDirectDraw4的物件.
1)引數IDirectDraw4 *是IDirectDraw4的物件指標.
2)引數LPCSTR,是Bitmap檔案全名.
3)使用例項:
  g_pDDPal = DDLoadPalette(g_pDD, "C:hanmybitmap1.bmp");
  if (g_pDDPal)
  g_pDDSPrimary->SetPalette(g_pDDPal);
4)詳情請參閱DDEX3-5.CPP

->DDLoadBitmap(IDirectDraw4 *pdd, LPCSTR szBitmap, int dx, int dy):
建立一塊含有指定Bitmap的IDirectDrawSurface4物件,先建立一塊足以容納指定Bitmap的IDirectDrawSurface4
物件.再將Bitmap讀入記憶體.
1)引數IDirectDraw4 *是物件指標.
2)引數LPCSTR,是Bitmap檔案全名.
3)dx, dy表示Bitmap大小.
4)使用例項:
  g_pDDSOne = DDLoadBitmap(g_pDD, "C:hanmybitmap.bmp", 640, 490);
  if(g_pDDSOne == NULL)
  {
  // Abort the program.
  }
5)沒有例項,多試試.

->DDReLoadBitmap(IDirectDrawSurface4 *pdds, LPCSTR szBitmap):
把一塊Bitmap讀入一個指定的IDirectDrawSurface4物件指標.
1)引數IDirectDrawSurface4 *是物件指標,將接收Bitmap.
2)引數LPCSTR,是Bitmap檔案全名.
3)使用例項:
  hRet = DDReLoadBitmap(g_pDDSOne, "C:hanmybitmap.bmp");
  if(FAILED(hret))
  {
  // Abort the program.
  }
4)詳情請參閱DDEX3-5.CPP
5)我用此函式最多.

->DDCopyBitmap(IDirectDrawSurface4 *pdds, HBITMAP hbm, int x, int y, int dx, int dy):
You don't need to know.

->DDColorMatch(IDirectDrawSurface4 *pdds, COLORREF rgb):
You don't need to know.

->DDSetColorKey(IDirectDrawSurface4 *pdds, COLORREF rgb):
在指定的引數IDirectDrawSurface4的物件指標上設定一transparent Colorkey.
1)引數IDirectDrawSurface4 *是物件指標,將接收ColorKey.
2)COLORREF, Colorkey的數值.
3)使用例項:
  hRet = DDReLoadBitmap(g_pDDSOne, "C:hanmybitmap.bmp");
  if(FAILED(hret))
  {
  // Abort the program.
  }
4)詳情請參閱DDEX3-5.CPP
5)我用此函式也很多.

For more information about those functions, you better observe the examples provided (DDEX1 - DDEX5) for
details.It is the easy way. All the implementations about those functions is in DDUTIL.CPP file.

6.BLIT, FLIP, &Color fill.
這三個應用是DirectDraw的主要應用.其功能為在BackBuffer上畫上Bitmap圖塊,或填上一定的顏色,
再翻到螢幕上.所用函式為:
1>.Blt和BltFast在BackBuffer上畫上Bitmap圖塊.

Example;
RECT rcRect;

rcRect.left = 0;
rcRect.top = 0;
rcRect.right = 640;
rcRect.bottom = 480;

 while(1) {
  ddrval = g_pDDSBack->BltFast(0, 0, g_pDDSOne, &rcRect, DDBLTFAST_WAIT);
  if(ddrval == DD_OK) 
  {
  break;
  }
  else if(ddrval == DDERR_SURFACELOST)
  {
  // use the restore all function!
  RestoreAll();
  }
}

我常用Blt function:

Example;
RECT srcRect = {0, 0, 640, 480};
RECT dstRect = {0, 0, 640, 480};

DDBLTFX tempFX; // you will fail if you don't have this!
tempFX.dwsize = sizeof(DDBLTFX); // you will fail if you don't have this!

 while(1) {
  ddrval = g_pDDSBack->Blt(dstRect, g_pDDSOne, srcRect, DDBLT_WAIT, &tempFX);
  if(ddrval == DD_OK) 
  {
  break;
  }
  else if(ddrval == DDERR_SURFACELOST)
  {
  // use the restore all function!
  RestoreAll();
  }
  else if(ddrval != DDERR_WASSTILLDRAWING)
  {
  break;
  }
}

若想使用透明色(COLORKEY),改DDBLTFAST_WAIT(BltFast的最後一個引數)為
DDBLTFAST_WAIT|DDBLTFAST_SRCCOLORKEY. 改DDBLT_WAIT(Blt的倒數第二個引數)為
DDBLT_WAIT | DDBLT_COLORSRC.

2.>用Blt在BackBuffer上填充顏色,(我從未成功過).

RECT dstRect = {0, 0, 640, 480};
DDBLTFX tempFX; // you will fail if you don't have this!
tempFX.dwsize = sizeof(DDBLTFX); // you will fail if you don't have this!
tempFX.dwFillColor = RGB(0, 255, 0); // green.

 while(1) {
  ddrval = g_pDDSBack->Blt(dstRect, NULL, NULL, DDBLT_WAIT | DDBLT_COLORFILL, &tempFX);
  if(ddrval == DD_OK) 
  {
  break;
  }
  else if(ddrval == DDERR_SURFACELOST)
  {
  // use the restore all function!
  RestoreAll();
  }
  else if(ddrval != DDERR_WASSTILLDRAWING)
  {
  break;
  }
}

這個例碼將在dstRect(0, 0, 640, 480)的長方形中填充綠色.長方形的大小和位置以你定義
的RECT物件確定.用Blt和BltFast的幾個弱點是:
1)當dstRect(目標長方塊)不和srcRect(源長方塊)大小一致,Blt和BltFast將十分慢.
2)當dstRect部分超出螢幕,如螢幕大小640X480,你想在長方塊{500, 500, 800, 600}中顯示
圖塊,此圖塊不將處顯,因為你定義的圖塊在螢幕以外!解決方法將在續篇(Advanced Section)中提出.

3.>使用Flip:
將準備好的BackBuffer翻到螢幕上,原來的Primar Surface變成BackBuffer.
 while(1) {
  ddrval = g_pDDSPrimary->Flip(NULL, DDFLIP_WAIT);
  if(ddrval == DD_OK) 
  {
  break;
  }
  else if(ddrval == DDERR_SURFACELOST)
  {
  // use the restore all function!
  RestoreAll();
  }
  else if(ddrval != DDERR_WASSTILLDRAWING)
  {
  break;
  }
}

4.總結:
1) 建立暫時的IDirectDraw物件.
2) 用COM中的QueryInterface來建立IDirectDraw4的物件.
3) SetCooperativeLevel (視窗或全屏).
4) SetDisplayMode(螢幕大小,色度,重新整理速度).
5) 建立primary surface(螢幕顯示記憶體),back buffer(幕後顯示記憶體),和off-screen sufaces(備用顯示記憶體,用於
儲存圖塊).
6) 使用ddutil檔案中的函式來讀入Bitmap,設定ColorKey(當你進行Blt操作,這種顏色將不被顯示).
7)  用Blt和BltFast將圖塊打入back buffer,用Flip來將準備好的back buffer翻成primary surface.原有的
primary surface翻成back buffer,等待下一輪Blt和BltFast的呼叫.

這些是DirectDraw版本6中的基本操作!我將寫一個續篇(Advanced Section).Enjoy programming!
請大膽提出問題,寫信給我或留言於留言板!感謝Doug Klopfenstein!他的Basic DirectDraw Programming
(譚翁編譯了這一文章!)給我很大幫助.


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-988059/,如需轉載,請註明出處,否則將追究法律責任。

相關文章