190 likes | 285 Views
DirectDraw and Bitmaps Part 1. CIS 487/587 Bruce R. Maxim UM-Dearborn. Screen Basics. A standard 640x480x8 screen buffer has 307,200 pixels Without 3D acceleration hardware, software-based rasterization is not practical for more than 256 colors
E N D
DirectDraw and BitmapsPart 1 CIS 487/587 Bruce R. Maxim UM-Dearborn
Screen Basics • A standard 640x480x8 screen buffer has 307,200 pixels • Without 3D acceleration hardware, software-based rasterization is not practical for more than 256 colors • For bitmap work this is 3D acceleration is a little less critical
16 Bit Encoding • There are several 16 bit high-color pixel encoding schemes • Alpha 5.5.5 • X 5.5.5 • 5.6.5 (most common) • Getting the pixel format right before plotting is important • You can choose to set it to 5.6.5 yourself if you wish
Writing a Screen Pixel • Lock the surface using Lock( ) • Build the 16-bit RGB word using the macro _RGB16BIT5.6.5 • Write the pixel using a USHORT pointer into VRAM • Unlock the primary surface using unlock
Ddraw 16-bit Pixel Plotting // done as global declarations // builds a 16 bit color value in 5.6.5 format (green dominate mode) #define _RGB16BIT565(r,g,b) ((b&31) + ((g&63) << 5) + ((r&31) << 11)) // initializes a direct draw struct #define DDRAW_INIT_STRUCT(ddstruct) {memset(&ddstruct,0,sizeof(ddstruct)); ddstruct.dwSize=sizeof(ddstruct); } inline void Plot_Pixel_Faster16(int x, int y, int red, int green, int blue, USHORT *video_buffer, int lpitch16) { // plots a pixel in 16-bit color mode assumes caller already locked the // surfaceand is sending a pointer and byte pitch to it USHORT pixel = _RGB16BIT565(red,green,blue); // first build up color WORD video_buffer[x + y*lpitch16] = pixel; // write the data } // end Plot_Pixel_Faster16
Game_Main( ) // clear ddsd and set size, never assume it's clean DDRAW_INIT_STRUCT(ddsd); // lock the primary surface if (FAILED(lpddsprimary->Lock(NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL))) return(0); int lpitch16 = (int)(ddsd.lPitch >> 1); USHORT *video_buffer = (USHORT *)ddsd.lpSurface; // plot the pixel once position and color is set Plot_Pixel_Faster16(x,y,red,green,blue,video_buffer,lpitch16); // now unlock the primary surface if (FAILED(lpddsprimary->Unlock(NULL))) return(0);
Game_Init( ) // 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 640x480x16 if (FAILED(lpdd->SetDisplayMode(SCREEN_WIDTH,SCREEN_HEIGHT,SCREEN_BPP,0,0))) return(0); // clear ddsd and set size memset(&ddsd,0,sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); ddsd.dwFlags = DDSD_CAPS; // enable valid fields ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; // request primary surface // create the primary surface if (FAILED(lpdd->CreateSurface(&ddsd, &lpddsprimary, NULL))) return(0);
Game_Shutdown( ) // blow away the primary surface if (lpddsprimary) { lpddsprimary->Release(); lpddsprimary = NULL; } // end if // blow away the IDirectDraw interface if (lpdd) { lpdd->Release(); lpdd = NULL; } // end if
Plotting 24 Bit Pixels inline void Plot_Pixel_24(int x, int y, int red, int green, int blue, UCHAR *video_buffer, int lpitch) { // function plots pixel in 24-bit color modeassumes caller locked surface // and is sending a pointer and byte pitch to it // in byte or 8-bit math the proper address is: 3*x + y*lpitch // this is the address of low order byte which is the Blue channel // since data is in RGB order DWORD pixel_addr = (x+x+x) + y*lpitch; // write the data, first blue video_buffer[pixel_addr] = blue; // now red video_buffer[pixel_addr+1] = green; // finally green video_buffer[pixel_addr+2] = red; } // end Plot_Pixel_24
Plotting 32 Bit Pixels // builds a 32 bit color value in A.8.8.8 format (8-bit alpha mode) #define _RGB32BIT(a,r,g,b) ((b)+ ((g)<<8) + ((r)<<16) + ((a)<<24)) inline void Plot_Pixel_32(int x, int y, int alpha,int red, int green, int blue, UINT *video_buffer, int lpitch32) { // this function plots a pixel in 32-bit color mode // assuming that the caller already locked the surface // and is sending a pointer and DWORD aligned pitch to it // first build up color WORD UINT pixel = _RGB32BIT(alpha,red,green,blue); // write the data video_buffer[x + y*lpitch32] = pixel; } // end Plot_Pixel_32
Animation and Bitmaps • DirectDraw gives you two choices for doing continuous animation • Page flipping (using primary and secondary surfaces) • Double buffering (using system memory for back buffer and programmer to flips the pages) • In both cases DirectDraw does the hard work of page flipping (regardless of whether VRAM is linear or non-linear)
Overview of Double Buffering • Game_Init( ) • new is used to allocate double buffer • Game_Sutdown( ) • Double buffer must be freed up • Game_Main( ) • Double buffer is erased and 5000 pixels are drawn “offline” • Check is made for linear memory and then buffer is copied to screen memory
Game_Init( ) // once the primary surface, cooperation, display mode, and palette // are defined // allocate double buffer if ((double_buffer=new UCHAR[SCREEN_WIDTH * SCREEN_HEIGHT])==NULL) return(0);
Game_Shutdown( ) // blow away the palette, surface, and direct draw interface first // release the memory used for double buffer if (double_buffer) { delete double_buffer; double_buffer = NULL; } // end if
Game_Main( ) UCHAR *primary_buffer = NULL; // alias to primary surface buffer // erase double buffer memset((void *)double_buffer,0, SCREEN_WIDTH*SCREEN_HEIGHT); double_buffer[x+y*SCREEN_WIDTH] = col; // plot at least one pixel // 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;
Game_Main( ) // 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;
Game_Main( ) // 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; } // end for } // end else // now unlock the primary surface if (FAILED(lpddsprimary->Unlock(NULL))) return(0);
Back Buffer Steps • Allocate memory for back buffer with same pixel dimensions as primary • In main loop erase back buffer • Perform game logic • Render the next frame in the back buffer • Copy the back buffer to the primary buffer (surface) • Synchronize display to desired frame rate • Go to step 2