前面的例子都是,直接在主表面上繪東西。對於動畫,直接在主表面上繪,會產生很嚴重的閃爍。解決的辦法是採用雙緩衝或後備緩衝。
雙緩衝
雙緩衝:在離屏緩衝中繪製圖像,然後將其拷貝到顯示錶面。
見下面代碼,先把數據放到double_buffer,最後再拷貝到主表面上。
下面Sleep(300);睡眠了0.3秒這麼長的時間是爲了更爲明顯的看到畫面的變化。
代碼下載
int Game_Main()
{
if (window_closed)
return 0;
if (KEYDOWN(VK_ESCAPE))
{
PostMessage(main_window_handle,WM_CLOSE,0,0);
return 0;
window_closed = 1;
}
memset((void *)double_buffer, 0, SCREEN_WIDTH * SCREEN_HEIGHT);
for (int index = 0; index < 5000; index++)
{
int x = rand() % SCREEN_WIDTH;
int y = rand() % SCREEN_HEIGHT;
UCHAR col = rand() % 256;
// 先把顏色在於緩衝區中
double_buffer[x + y * SCREEN_WIDTH] = col;
}
DDRAW_INIT_STRUCT(ddsd);
lpddsprimary->Lock(NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT,NULL);
// 主表面顯存指針
UCHAR * primary_buffer = (UCHAR *)ddsd.lpSurface;
// 再把緩衝區的內容拷貝到主表面。
if (ddsd.lPitch == SCREEN_WIDTH)
{
memcpy((void *)primary_buffer, (void *)double_buffer, SCREEN_WIDTH * SCREEN_HEIGHT);
}
else
{
UCHAR *dest_ptr = primary_buffer;
UCHAR *src_ptr = double_buffer;
// 一行一行的拷貝
for (int y=0; y < SCREEN_HEIGHT; y++)
{
memcpy((void *)dest_ptr, (void *)src_ptr, SCREEN_WIDTH);
dest_ptr+= ddsd.lPitch;
src_ptr += SCREEN_WIDTH;
}
}
if (FAILED(lpddsprimary->Unlock(NULL)))
return 0;
Sleep(300);
return 1;
}
後備緩衝
後備緩衝也是離屏表面的一種。它是一些用在動畫鏈中的表面,它們具有和主表面相同的尺寸和色深。當創建主表面的時候也創建它們。它和主表面進行頁面切換,這比雙緩衝方案下所需做的內存拷貝要快得多。
創建一個關聯後備緩衝的主表面步驟(複雜表面):
- 把DDSD_BACKBUFFERCOUNT加到dwFlagss標誌字段,把DDSURFACEDESC2 結構的dwBackBufferCount字段設爲有效。(它可以設置後備緩衝的數目)
- 把DDSCAPS_COMPLEX | DDSCAPS_FLIP加到DDSURFACEDESC2 結構的ddsCaps.dwCaps
- 創建主表面。
- 得到後備緩衝有再個方法。設置DDSCAPS2 ddscaps.dwCaps = DDSCAPS_BACKBUFFER; 或 ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
然後調用 GetAttachedSurface(&ddsd.ddsCaps, &lpddsback)得到後備緩衝。
見下面代碼:
DDRAW_INIT_STRUCT(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT; // 增加DDSD_BACKBUFFERCOUNT 表明dwBackBufferCount有效
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP; // 多了 DDSCAPS_COMPLEX | DDSCAPS_FLIP
ddsd.dwBackBufferCount = 1; // 後備緩衝的個數
if (FAILED(lpdd->CreateSurface(&ddsd, &lpddsprimary, NULL)))
return 0;
// 請求一個後備緩衝
ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
// 得到後備緩衝
if (FAILED(lpddsprimary->GetAttachedSurface(&ddsd.ddsCaps, &lpddsback)))
return 0;
以上代碼。Lpddsprimary指向主表面,lpddsback指向後備緩衝表面。
對後備緩衝表面進行存取,也需要對其加鎖和解鎖。在對後備緩衝表面繪圖後,把怎麼把所之圖顯示到主表面呢。這就要用到頁面切換了(而且這個切換是由硬件完成的)。
用主表面調用下面的函數來切換。(注意在頁面切換前主表面或後備緩衝表面都必須被解鎖)
HRESULT Flip(LPDIRECTDRAWSURFACE7 lpDDSurfaceTargetOverride ,DWORD dwFlags);
第一個參數:用來覆蓋切換鏈,實現切換到另外一個表面,不是切換到同主表面想關聯的後備緩衝,一般爲NULL。
第二個參數:控制標誌。如下:
DDFLIP_INTERVAL2
|
2次垂直逆程後切換
|
DDFLIP_INTERVAL3
|
3次垂直逆程後切換
|
DDFLIP_INTERVAL4
|
4次垂直逆程後切換
|
這些標誌 在DDCAPS結構中設置了DDCAPS2_FLIPINTERVAL才能起作用。默認值是1.
這些標誌 表明在兩個切換頁之間等待多少垂直逆程,只有在指定的垂直逆程數目達到了,DDraw才爲切換的每一頁返回DERR_WASSTILLDRAWING
DDFLIP_NOVSYNC:執行物理切換時儘量靠近下一條掃描線。
DDFLIP_WAIT:強迫硬件不 在出現問題時立即返回,而是等待直到頁面切換能夠進行爲止。一般就用這個標誌比較多。
代碼下載
int MainGame()
{
if (window_closed)
return 0;
if (KEYDOWN(VK_ESCAPE))
{
PostMessage(main_window_handle, WM_CLOSE, 0, 0);
window_closed = 1;
}
DDRAW_INIT_STRUCT(ddsd);
lpddsback->Lock(NULL,&ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT,NULL);
UCHAR *back_buffer = (UCHAR *)ddsd.lpSurface;
// 清除後備緩衝數據
if (ddsd.lPitch == SCREEN_WIDTH)
memset(back_buffer, 0, SCREEN_WIDTH * SCREEN_HEIGHT);
else
{
UCHAR *dest_ptr = back_buffer;
for (int y=0; y<SCREEN_HEIGHT; y++)
{
memset(dest_ptr, 0, SCREEN_WIDTH);
dest_ptr+=ddsd.lPitch;
}
}
for (int index=0; index < 5000; index++)
{
int x = rand() % SCREEN_WIDTH;
int y = rand() % SCREEN_HEIGHT;
UCHAR col = rand() % 256;
back_buffer[x + y * ddsd.lPitch] = col;
}
if (FAILED(lpddsback->Unlock(NULL)))
return 0;
// 切換主表面,是主表面調用Flip,另外要注意的是調用Flip之前,要先解鎖。
while (FAILED(lpddsprimary->Flip(NULL, DDFLIP_WAIT)));
Sleep(500);
return 1
}
轉載請保留下面鏈接
http://www.cnblogs.com/fangyukuan/archive/2011/06/07/2074713.html