DircetDraw c/c++ 使用指導(一) (轉)

gugu99發表於2008-01-05
DircetDraw c/c++ 使用指導(一) (轉)[@more@]

DircetDraw c/c++ 使用指導


310cdt 譯
邊看邊譯,譯完就拿了上來,見笑了

這是一系列的DirectDraw的指南,教你一步步的去構建一個簡單的DirectDraw應用.這個指南用到了sdk包提供的很多DirectDraw的例子.這些例子展示了怎樣設定DirectDraw,怎樣用DirectDraw方法實現一般任務:


注意:這些指南中的例子是用c++寫的.如果你使用的是c,請進行適當的改變,以能進行成功的編譯.你需要把vtable和this指標新增到介面方法中.

 

1.DirectDraw基礎用法

要使用DirectDraw,你必須先建立一個代表顯示介面的DirectDraw例項.然後,你就可以透過介面的方法來操縱這個.你可能會需要建立一個或更多的DirectDraw平面物件(DirectDraw surface )的例項
來在圖形裝置上顯示你的應用.

為了演示這個,例子DDEx1 sample((SDK )SamplesMultimediaDDrawSrcDdex1)演示了以下幾步:

step 1:建立一個DirectDraw物件
想建立一個DirectDraw物件的例項,你的應用程式必須在InitApp中使用DirectDrawCreateEx函式,就像例程ddex1中一樣.DirectDrawCreateEx函式包括4個引數.第一個是:顯示裝置全域性唯一標識(GUID).GUID大部分情況下是設為NULL,選擇預設的顯示裝置.第二個引數是:包含的是標示DirectDraw物件是否建立的指標的地址.第三個引數是IDirectDraw7介面的參考標示.必須設為IID_IDirectDraw7.第四個引數是設定為NULL的,是為了將來的擴充套件做準備的.
以下的例子展示了怎樣建立DirectDraw物件,並判斷建立是否成功.
hRet = DirectDrawCreateEx(NULL, (VOID**)&g_pDD, IID_IDirectDraw7, NULL);
if(hRet == DD_OK)
{
  // g_pDD is a valid DirectDraw object.
}
else
{
  // The DirectDraw object could not be created.
}

 

step2:決定程式的行為
在你改變顯示方法前,你必須至少在IDirectDraw7::SetCerativeLevel函式中指定dwFlags引數(DDSCL_EXCLUSIVE 和 DDSCL_FULLSCREEN).這樣,你的應用程式就得到了顯示裝置的全部控制權,其他的程式不能共享.DDSCL_FULLSCREEN讓你的應用程式在全螢幕下執行.你的程式覆蓋了桌面,並且只有你的程式能在螢幕上輸出.但是,桌面仍然是有效的.(按ALT+TAB切換到桌面)

下面的例子演示了SetCooperativeLevel函式的用法.
HRESULT  hRet;
LPDIRECTDRAW7 g_pDD;  // already created by DirectDrawCreateEx

hRet = g_pDD->SetCooperativeLevel(hWnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
if (hRet != DD_OK)
{
  // Exclusive mode was succesul.
}
else
{
  // Exclusive mode was not successful.
  // The application can still run, however.
}

如果 SetCooperativeLevel不返回DD_OK,你可以指定為DDSCL_NORMAL繼續執行你的程式.但是,你的程式不再是獨佔模式,而且,可能會無法完成你的一部分要求.在這種情況下,你最好顯示一個對話方塊讓決定是否要繼續.
如果你設定的是全螢幕獨佔合作級別,你必須把程式的視窗控制程式碼傳遞給SetCooperativeLevel,這樣可以讓有能力決定你的程式是否異常終止.

 

step3 :改變顯示模式
接下來,你可以用IDirectDraw7::SetDisplayMode函式來改變顯示模式.下面的例子將演示怎樣把顯示模式設定為640 × 480 × 8 bits per pixel (bpp).
HRESULT  hRet;
LPDIRECTDRAW7 g_pDD;  // already created by DirectDrawCreateEx

hRet = g_pDD->SetDisplayMode(640, 480, 8, 0, 0);
if (hRet != DD_OK)
{
  // The display mode changed successfully.
}
else
{
  // The display mode cannot be changed.
  // The mode is either not supported or
  // another application has exclusive mode.
}

當你設定顯示模式的時候,你應該確定使用者的是否支援這樣的模式.你可以設定一個能被大部分顯示介面卡支援的標準的模式.例如:你的程式可以以640 × 480 × 8作為備選模式,設計成為可以在所有系統上執行.
注意:IDirectDraw7::SetDisplayMode返回一個DDERR INVALIDMODE錯誤值,如果顯示介面卡無法切換到想要的模式時.你可以在試圖改變顯示模式前,用IDirectDraw7::EnumDisplayModes函式看一下使用者顯示介面卡的能力.


step4:建立頁(flip surface)
設定完顯示模式以後,你應該建立一個平面.例程:DDEx1用IDirectDraw7::SetCooperativeLevel 設定為獨佔模式.所以,你可以建立交換頁(flipping surfaces).如果你設定的是DDSCL_NORMAL模式,你可以建立一個可以塊移動(blit)的平面.建立交換頁(flipping surfaces)需要以下步驟:
(4.1)定義需求的平面:
第一步是以DDSURFACEDESC2結構定義一個需求的平面.下面的例子演示了結構的定義和標誌位的設定:
// Create the primary surface with one 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;

在這個例子中,dwSize成員是DDSURFACEDESC2結構的大小.這是防止你用到的DirectDraw方法返回無效成員的錯誤.(dwSize是準備給將來的DDSURFACEDESC2結構的擴充套件用的)
dwFlags成員決定的DDSURFACEDESC2結構中那些成員將被填充有效的資訊.例如在DDEx1中,dwFlags被設為你想要用DDSCAPS結構(DDSD_CAPS)和你想建立一個後臺緩衝(back buffer)(DDSD_BACKBUFFERCOUNT)
dwCaps成員在例子中標示一個將要在DDSCAPS結構中使用的標誌位.在這種情況下,他指定一個主平面(primary surface DDSCAPS_PRIMARYSURFACE),一個交換頁(flipping surface DDSCAPS_FLIP),一個合成表面(complex surface DDSCAPS_COMPLEX).
最後,例子指定了一個後臺緩衝.後臺緩衝就是實際的繪圖操作先在那裡完成,然後,再的翻動(flip)到主平面(primary surface)上.在DDEx1中,後臺緩衝的數目是1.其實,你要你的視訊記憶體允許,你想建幾個就建幾個.你想知道更多的關於建立大於1塊緩衝的資訊,可以去看  "triple buffering".
建立的"平面"佔用的空間,可以是系統也可以是視訊記憶體.如果應用程式使用的空間超出了視訊記憶體,DirectDraw就會使用系統記憶體.(例如你指定多塊快取在一個僅有1MB視訊記憶體的是配器上).你也可以這樣設定DDSCAPS結構的dwCaps成員,設成DDSCAPS_VOMEMEORY或DDCAPS_SYSTEMMEMORY以達到只用視訊記憶體或只用記憶體.(如指定用視訊記憶體,而視訊記憶體不夠,IDirectDraw7::CreateSurface返回一個DDERR_OUTOFVIDEOMEMORY錯誤)
(4.2)建立平面
在填充完DDSURFACEDESC2結構後,你就可以用他和g_pDD指標(DirectDrawCreateEx函式建立的DirectDraw物件的指標)IDirectDraw7::CreateSurface方法,就像下面 :
hRet = g_pDD->CreateSurface(&ddsd, &g_pDDSPrimary, NULL);
if (hRet != DD_OK)
{
  // g_pDDSPrimary points to the new surface.
}
else
{
  // The surface was not created.
  return FALSE;
}
g_pDDSPrimary引數將指向由CreateSurface函式返回的主平面(primary surface)的地址,如果呼叫成功的話.
指向主平面(primary surface)的指標有效後,就可以呼叫IDirectDrawSurface7::GetAttachedSurface方法去得到一個指向緩衝的指標.如下:
ZeroMemory(&ddscaps, sizeof(ddscaps));
ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
hRet = g_pDDSPrimary->GetAttachedSurface(&ddscaps, &g_pDDSBack);
if (hRet != DD_OK)
{
  // g_pDDSBack points to the back buffer.
}
else
{
  return FALSE;
}
如果IDirectDrawSurface7::GetAttachedSurface呼叫成功,g_pDDSBack引數將指向快取區.

 

step5:在平面上輸出
在主平面(primary surface)和後臺緩衝(back buffer)建立完成後,例子DDEx1用windows標準GDI函式輸出了一些文字在緩衝上.如下:
if (g_pDDSBack->GetDC(&hdc) == DD_OK)
  {
  SetBkColor(hdc, RGB(0, 0, 255));
  SetTextColor(hdc, RGB(255, 255, 0));
  if (phase)
  {
  GetClientRect(hWnd, &rc);
  GetTextExtentPoint(hdc, szMsg, lstrlen(szMsg), &size);
  TextOut(hdc, (rc.right - size.cx) / 2, (rc.bottom - size.cy) / 2,
  szMsg, sizeof(szMsg) - 1);
  TextOut(hdc, 0, 0, szFrontMsg, lstrlen(szFrontMsg));
  phase = 0;
  }
  else
  {
  TextOut(hdc, 0, 0, szBackMsg, lstrlen(szBackMsg));
  phase = 1;
  }
  g_pDDSBack->ReleaseDC(hdc);
  }

這個例子使用the IDirectDrawSurface7::GetDC方法獲得裝置上下文的控制程式碼,並且,為了準備寫入,將緩衝上鎖.如果你不準備用需要裝置上下文控制程式碼的windows函式,你可以使用IDirectDrawSurface7::Lock  IDirectDrawSurface7::Unlock方法加解鎖.
接下來,phase變數決定了應該輸出到主快取訊息(primary buffer message)還是後臺快取訊息(back buffer message).如果phase=1,輸出
主快取訊息,且設phase=0.如果phase=0,輸出後臺快取訊息,且設phase=1.
當訊息輸出到快取區後,用IDirectDrawSurface7::ReleaseDC 方法將快取區解鎖.
對建立平面的記憶體上鎖,是保證你的程式和系統不能同時對此記憶體進行存取.這防止你寫入"平面"記憶體事發生錯誤.另外,不對"平面"記憶體解鎖,你的程式將無法翻轉平面.
平面被加鎖之後,例子中用了標準windowsGDI函式:SetBkColor設定背景色,SetTextColor設定在背景上顯示的字的顏色,用TextOut在"表面"上輸出文字和背景色.
當文字被輸出到快取之後,例子應用IDirectDrawSurface7::ReleaseDC方法解鎖"表面"並且釋放控制程式碼.不論在什麼時候你的程式完成了對快取的寫入,一定要呼叫IDirectDrawSurface7::ReleaseDC 或IDirectDrawSurface7::Unlock之一,具體用哪個,由你的程式而定.不解鎖,你的程式將不能翻轉平面.
注意:用IDirectDrawSurface7::Unlock對"平面"解鎖後,指向"平面"的指標將會無效.你必須再用IDirectDrawSurface7::lock去獲得一個有效的指標.

 

step:6 翻轉平面
在DDEx1中,WM_TIMER訊息引發從快取到主平面的翻轉.當"平面"記憶體解鎖後,你就可以用IDirectDrawSurface7::Flip方法實現從快取到主平面的翻轉.如下:
case WM_TIMER:
  // Update and flip surfaces
  if (g_bActive && TIMER_ID == wParam)
  {
  UpdateFrame(hWnd);
  while (TRUE)
  {
  hRet = g_pDDSPrimary->Flip(NULL, 0);
  if (hRet == DD_OK)
  break;
  if (hRet == DDERR_SURFACELOST)
  {
  hRet = g_pDDSPrimary->Restore();
  if (hRet != DD_OK)
  break;
  }
  if (hRet != DDERR_WASSTILLDRAWING)
  break;
  }
  }
  break;

在例子中,g_pDDSPrimary引數指示主平面和與他連線的快取.當IDirectDrawSurface7::Flip被呼叫,前後平面將會交換(只是交換指標,沒有實際的資料交換).如果翻轉成功,返回DD_OK,跳出迴圈.
如果翻轉返回的是DDERR_SURFACELOST值,就試圖用IDirectDrawSurface7::Restore 方法儲存平面.如果儲存成功,程式迴圈到呼叫IDirectDrawSurface7::Flip,再試一次.如果儲存不成功,程式跳出迴圈,返回一個錯誤.
注意:當你呼叫IDirectDrawSurface7::Flip 時,翻轉動作並不能馬上完成.如果,前一個翻轉動作還沒有完成,IDirectDrawSurface7::Flip 會返回DDERR_WASSTILLDRAWING.在這個例子中,IDirectDrawSurface7::Flip會一直呼叫直到返回DD_OK.


step7:釋放DirectDraw物件
當你按下F12鍵時,例子DDEx1將在退出程式前處理WM_DESTROY訊息.這個訊息將呼叫ReleaseAllObjects函式,這個函式包括了多個Release呼叫.像下面一樣:
static void
ReleaseAllObjects(void)
{
  if (g_pDD != NULL)
  {
  if (g_pDDSPrimary != NULL)
  {
  g_pDDSPrimary->Release();
  g_pDDSPrimary = NULL;
  }
  g_pDD->Release();
  g_pDD = NULL;
  }
}
這個程式檢測DirectDraw物件的指標g_pDD和DirectDraw平面物件的指標g_pDDSPrimary是否為NULL.然後,呼叫IDirectDrawSurface7::Release方法令DirectDraw平面物件的reference count(可以認為是建立的物件的數目)數減1,這使得reference count減為0,DirectDraw平面物件就釋放了.然後,還需將他的指標值設為NULL.接下來,程式呼叫IDirectDraw7::Release,同樣是令DirectDraw的reference count減1,然後然後....全銷燬.


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

相關文章