7.3.双缓冲

实现双缓冲微不足道.需要你完成的只是制订一块同主DirectDraw画面相同尺寸的内存.将动画的每一屏幕画到它上面,然后拷贝双缓冲到主显示画.

 

下面就是一个建立双缓冲的代码:

UCHAR *double_buffer = (UCHAR)mallic(640*400);

或者用新的C++语言操作符:

UCHAR *double_buffer = new UCHAR[640*400];

两种方法都可以得到一个双缓冲指针指向的307200字节大小的线性可寻址内存阵列.

对一个位于(x,y)的像素点寻址,只需要

double_buffer[x+640*y]  =

 

但主显示画面可能每线有额外内存,发生寻址错误,这时就需要自己一个更好的双缓冲拷贝函数,检测主礓面的步长是否640.

是,就采用memcpy()函数,不是就一行行拷贝.虽然慢了一点,但却能做到最好...下面是程序代码

// cacn we use a straight memory copy?

if(mempitch == 640)

{

mamcpy((void *)promary_buffer,(void *)double_buffer,640*400);

}

else

{

//copy line by line, bummer!

for(int y=0; i<480; y++){

//copy next line of 640 bytes

memcpy((void *)promary_buffer,(void * double_buffer,640*400);

 

//now for the tricky part...

// advance each pointer ahead to next line

primary_buffer+=mempitch;

 

// we know that we need to advance 640 bytes per line

double_buffer+=640;

} // end for y

} // end else

 

 

// DEMO7_4.CPP 8-bit double buffering demo // INCLUDES /////////////////////////////////////////////// #define WIN32_LEAN_AND_MEAN // just say no to MFC #define INITGUID #include <windows.h> // include important windows stuff #include <windowsx.h> #include <mmsystem.h> #include <iostream> // include important C/C++ stuff #include <conio.h> #include <stdlib.h> #include <malloc.h> #include <memory.h> #include <string.h> #include <stdarg.h> #include <stdio.h> #include <math.h> #include <io.h> #include <fcntl.h> #include <ddraw.h> // include directdraw // DEFINES //////////////////////////////////////////////// // defines for windows #define WINDOW_CLASS_NAME "WINCLASS1" // default screen size #define SCREEN_WIDTH 640 // size of screen #define SCREEN_HEIGHT 480 #define SCREEN_BPP 8 // bits per pixel // TYPES ////////////////////////////////////////////////////// // basic unsigned types typedef unsigned short USHORT; typedef unsigned short WORD; typedef unsigned char UCHAR; typedef unsigned char BYTE; // MACROS ///////////////////////////////////////////////// #define KEYDOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0) #define KEYUP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1) // initializes a direct draw struct #define DDRAW_INIT_STRUCT(ddstruct) { memset(&ddstruct,0,sizeof(ddstruct)); ddstruct.dwSize=sizeof(ddstruct); } // GLOBALS //////////////////////////////////////////////// HWND main_window_handle = NULL; // globally track main window int window_closed = 0; // tracks if window is closed HINSTANCE hinstance_app = NULL; // globally track hinstance // directdraw stuff LPDIRECTDRAW7 lpdd = NULL; // dd object LPDIRECTDRAWSURFACE7 lpddsprimary = NULL; // dd primary surface LPDIRECTDRAWSURFACE7 lpddsback = NULL; // dd back surface LPDIRECTDRAWPALETTE lpddpal = NULL; // a pointer to the created dd palette LPDIRECTDRAWCLIPPER lpddclipper = NULL; // dd clipper PALETTEENTRY palette[256]; // color palette PALETTEENTRY save_palette[256]; // used to save palettes DDSURFACEDESC2 ddsd; // a direct draw surface description struct DDBLTFX ddbltfx; // used to fill DDSCAPS2 ddscaps; // a direct draw surface capabilities struct HRESULT ddrval; // result back from dd calls DWORD start_clock_count = 0; // used for timing UCHAR *double_buffer = NULL; // pointer to double buffer char buffer[80]; // general printing buffer // FUNCTIONS ////////////////////////////////////////////// LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { // this is the main message handler of the system PAINTSTRUCT ps; // used in WM_PAINT HDC hdc; // handle to a device context char buffer[80]; // used to print strings // what is the message switch(msg) { case WM_CREATE: { // do initialization stuff here // return success return(0); } break; case WM_PAINT: { // simply validate the window hdc = BeginPaint(hwnd,&ps); // end painting EndPaint(hwnd,&ps); // return success return(0); } break; case WM_DESTROY: { // kill the application, this sends a WM_QUIT message PostQuitMessage(0); // return success return(0); } break; default:break; } // end switch // process any messages that we didn't take care of return (DefWindowProc(hwnd, msg, wparam, lparam)); } // end WinProc /////////////////////////////////////////////////////////// int Game_Main(void *parms = NULL, int num_parms = 0) { // this is the main loop of the game, do all your processing // here UCHAR *primary_buffer = NULL; // used as alias to primary surface buffer // make sure this isn't executed again if (window_closed) return(0); // for now test if user is hitting ESC and send WM_CLOSE if (KEYDOWN(VK_ESCAPE)) { PostMessage(main_window_handle,WM_CLOSE,0,0); window_closed = 1; } // end if // erase double buffer memset((void *)double_buffer,0, SCREEN_WIDTH*SCREEN_HEIGHT); // you would perform game logic... // draw the next frame into the double buffer // plot 5000 random pixels 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; } // end for index // copy the double buffer into the primary buffer DDRAW_INIT_STRUCT(ddsd); // lock the primary surface lpddsprimary->Lock(NULL,&ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT,NULL); // get video pointer to primary surfce primary_buffer = (UCHAR *)ddsd.lpSurface; // test if memory is linear if (ddsd.lPitch == SCREEN_WIDTH) { // copy memory from double buffer to primary buffer memcpy((void *)primary_buffer, (void *)double_buffer, SCREEN_WIDTH*SCREEN_HEIGHT); } // end if else { // non-linear // make copy of source and destination addresses UCHAR *dest_ptr = primary_buffer; UCHAR *src_ptr = double_buffer; // memory is non-linear, copy line by line for (int y=0; y < SCREEN_HEIGHT; y++) { // copy line memcpy((void *)dest_ptr, (void *)src_ptr, SCREEN_WIDTH); // advance pointers to next line dest_ptr+=ddsd.lPitch; src_ptr +=SCREEN_WIDTH; // note: the above code code be replaced with the simpler // memcpy(&primary_buffer[y*ddsd.lPitch], double_buffer[y*SCREEN_WIDTH], SCREEN_WIDTH); // but it is much slower due to the recalculation and multiplication each cycle } // end for } // end else // now unlock the primary surface if (FAILED(lpddsprimary->Unlock(NULL))) return(0); // wait a sec Sleep(500); // return success or failure or your own return code here return(1); } // end Game_Main //////////////////////////////////////////////////////////// int Game_Init(void *parms = NULL, int num_parms = 0) { // this is called once after the initial window is created and // before the main event loop is entered, do all your initialization // here // create IDirectDraw interface 7.0 object and test for error if (FAILED(DirectDrawCreateEx(NULL, (void **)&lpdd, IID_IDirectDraw7, NULL))) return(0); // set cooperation to full screen if (FAILED(lpdd->SetCooperativeLevel(main_window_handle, DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX | DDSCL_EXCLUSIVE | DDSCL_ALLOWREBOOT))) return(0); // set display mode to 640x480x8 if (FAILED(lpdd->SetDisplayMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP,0,0))) return(0); // clear ddsd and set size DDRAW_INIT_STRUCT(ddsd); // enable valid fields ddsd.dwFlags = DDSD_CAPS; // request primary surface ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; // create the primary surface if (FAILED(lpdd->CreateSurface(&ddsd, &lpddsprimary, NULL))) return(0); // build up the palette data array for (int color=1; color < 255; color++) { // fill with random RGB values palette[color].peRed = rand()%256; palette[color].peGreen = rand()%256; palette[color].peBlue = rand()%256; // set flags field to PC_NOCOLLAPSE palette[color].peFlags = PC_NOCOLLAPSE; } // end for color // now fill in entry 0 and 255 with black and white palette[0].peRed = 0; palette[0].peGreen = 0; palette[0].peBlue = 0; palette[0].peFlags = PC_NOCOLLAPSE; palette[255].peRed = 255; palette[255].peGreen = 255; palette[255].peBlue = 255; palette[255].peFlags = PC_NOCOLLAPSE; // create the palette object if (FAILED(lpdd->CreatePalette(DDPCAPS_8BIT | DDPCAPS_ALLOW256 | DDPCAPS_INITIALIZE, palette,&lpddpal, NULL))) return(0); // finally attach the palette to the primary surface if (FAILED(lpddsprimary->SetPalette(lpddpal))) return(0); // allocate double buffer if ((double_buffer=new UCHAR[SCREEN_WIDTH * SCREEN_HEIGHT])==NULL) return(0); // return success or failure or your own return code here return(1); } // end Game_Init ///////////////////////////////////////////////////////////// int Game_Shutdown(void *parms = NULL, int num_parms = 0) { // this is called after the game is exited and the main event // loop while is exited, do all you cleanup and shutdown here // first the palette if (lpddpal) { lpddpal->Release(); lpddpal = NULL; } // end if // now the primary surface if (lpddsprimary) { lpddsprimary->Release(); lpddsprimary = NULL; } // end if // now blow away the IDirectDraw4 interface if (lpdd) { lpdd->Release(); lpdd = NULL; } // end if // release the memory used for double buffer if (double_buffer) { delete double_buffer; double_buffer = NULL; } // end if // return success or failure or your own return code here return(1); } // end Game_Shutdown // WINMAIN //////////////////////////////////////////////// int WINAPI WinMain( HINSTANCE hinstance, HINSTANCE hprevinstance, LPSTR lpcmdline, int ncmdshow) { WNDCLASSEX winclass; // this will hold the class we create HWND hwnd; // generic window handle MSG msg; // generic message HDC hdc; // graphics device context // first fill in the window class stucture winclass.cbSize = sizeof(WNDCLASSEX); winclass.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW; winclass.lpfnWndProc = WindowProc; winclass.cbClsExtra = 0; winclass.cbWndExtra = 0; winclass.hInstance = hinstance; winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); winclass.hCursor = LoadCursor(NULL, IDC_ARROW); winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); winclass.lpszMenuName = NULL; winclass.lpszClassName = (LPCWSTR)WINDOW_CLASS_NAME; winclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); // save hinstance in global hinstance_app = hinstance; // register the window class if (!RegisterClassEx(&winclass)) return(0); // create the window if (!(hwnd = CreateWindowEx(NULL, // extended style (LPCWSTR)WINDOW_CLASS_NAME, // class L"DirectDraw 8-Bit Double Buffering Demo", // title WS_POPUP | WS_VISIBLE, 0,0, // initial x,y SCREEN_WIDTH,SCREEN_HEIGHT, // initial width, height NULL, // handle to parent NULL, // handle to menu hinstance,// instance of this application NULL))) // extra creation parms return(0); // save main window handle main_window_handle = hwnd; // initialize game here Game_Init(); // enter main event loop while(TRUE) { // test if there is a message in queue, if so get it if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) { // test if this is a quit if (msg.message == WM_QUIT) break; // translate any accelerator keys TranslateMessage(&msg); // send the message to the window proc DispatchMessage(&msg); } // end if // main game processing goes here Game_Main(); } // end while // closedown game here Game_Shutdown(); // return to Windows like this return(msg.wParam); } // end WinMain ///////////////////////////////////////////////////////////

你可能感兴趣的:(7.3.双缓冲)