1 directdraw and bitmaps part 2 cis 487/587 bruce r. maxim um-dearborn

33
1 DirectDraw and Bitmaps Part 2 CIS 487/587 Bruce R. Maxim UM-Dearborn

Post on 21-Dec-2015

218 views

Category:

Documents


0 download

TRANSCRIPT

1

DirectDraw and BitmapsPart 2

CIS 487/587

Bruce R. Maxim

UM-Dearborn

2

Page Flipping

1. Clear back buffer

2. Render scene to back buffer surface

3. Flip primary surface with back buffer surface

4. Lock to frame rate (e.g. 30 fps)

5. Repeat step 1

3

Creating Primary Surface with Back Buffer

• Add DDSD_BACKBUFFERCOUNT to the dwFlags field so DirectDraw can check number surfaces at creation

• Add DFDSCAPS_COMPLEX and DDSCAPS_FLIP to capabilities word of DDSURFACE2 contained in ddsCaps.dwCaps filed

• Create primary surface as usual and request attached buffer using GetAttachedSurface( )

4

LaMothe Examples

5

Inside Game_Init( )// enable valid fields

ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;

// set the backbuffer count field for double buffering

ddsd.dwBackBufferCount = 1;

// request a complex, flippable

ddsd.ddsCaps.dwCaps =

DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP;

// create the primary surface

if (FAILED(lpdd->CreateSurface(&ddsd, &lpddsprimary, NULL)))

return(0);

// now query for attached surface from the primary surface

// this line is needed by the call

ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;

// get the attached back buffer surface

if (FAILED(lpddsprimary->GetAttachedSurface(&ddsd.ddsCaps, &lpddsback)))

return(0);

6

Inside Game_Shutdown( )// first the paletteif (lpddpal){ lpddpal->Release(); lpddpal = NULL;} // end if // now the back buffer surfaceif (lpddsback){ lpddsback->Release(); lpddsback = NULL;} // end if // now the primary surfaceif (lpddsprimary){ lpddsprimary->Release(); lpddsprimary = NULL;} // end if// then the DirectDrawInterface

7

Inside Game_Main( )// draw the next frame into the back buffer, notice that we// must use the lpitch since it's a surface and may not be linear // plot 5000 random pixelsfor (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;} // end for index // unlock the back bufferif (FAILED(lpddsback->Unlock(NULL))) return(0); // perform the flip (both primary and back buffer must be unlockedwhile (FAILED(lpddsprimary->Flip(NULL, DDFLIP_WAIT)));

8

Using the Blitter

• The blitter can be used to paste bit images from off screen surfaces to the primary surface

• The function Blt( ) does blitting using DirectDraw clippers

• The function BltFast( ) does not do clipping and runs faster

9

Blitting to do Simple 8 Bit Fills

• Place the color index or RGB color you want to use to fill the surface in the dwFillColor field of a DDBLTFX struct

• Define a RECT in the area you want to fill on the destination surface

• Call Blt( ) from destination surface interface pointer using control falgs DDBT_COLORFILL | DDBLT_WAIT

10

Inside Game_Init( )// create IDirectDraw interface 7.0 object

// set cooperation to full screen

// set display mode

// clear ddsd and set size

DDRAW_INIT_STRUCT(ddsd);  

// enable valid fields

ddsd.dwFlags = DDSD_CAPS;

// request a complex, flippable

ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;

 

// create the primary surface

11

Inside Game_Main( )

DDBLTFX ddbltfx; // the blitter fx structure

RECT dest_rect; // used to hold the destination RECT

// first initialize the DDBLTFX structure

DDRAW_INIT_STRUCT(ddbltfx);

 

// now set the color word info to the color we desire

// in this case, we are assuming an 8-bit mode, hence,

// well use a color index from 0-255

ddbltfx.dwFillColor =

_RGB16BIT565(rand()%256, rand()%256, rand()%256);

12

Inside Game_Main( )// now set up the RECT structure to fill the region from

// (x1,y1) to (x2,y2) on the destination surface

dest_rect.left = x1;

dest_rect.top = y1;

dest_rect.right = x2;

dest_rect.bottom = y2;

 

// make the blitter call

if (FAILED(lpddsprimary->Blt(&dest_rect, // pointer to dest RECT

NULL, // pointer to source surface

NULL, // pointer to source RECT

DDBLT_COLORFILL | DDBLT_WAIT,

// do color fill wait if you have to

&ddbltfx))) // pointer to DDBLTFX info

return(0);

13

Inside Game_Main( )

• Please note, in this example the call to Blt( ) has NULL pointers to both the surface and the source rectangle

• The reason for this is that the color fills are handled by the blitter hardware support (or emulation if needed)

14

Copying Bitmaps Between Surfaces

• When using the Blt( ) function you are sending both a source and destination rectangle to use in performing the blit

• Blitting when the source and destination surfaces are different is the basis for most sprite engines

• A sprite is a bitmap the seems to move on the screen

15

Game_Main( )RECT source_rect, // used to hold the destination RECT dest_rect; // used to hold the destination RECT

// now set up the RECT structure to fill the region from// (x1,y1) to (x2,y2) on the source surfacesource_rect.left = x1;source_rect.top = y1;source_rect.right = x2;source_rect.bottom = y2; // now set up the RECT structure to fill the region from// (x3,y3) to (x4,y4) on the destination surfacedest_rect.left = x3;dest_rect.top = y3;dest_rect.right = x4;dest_rect.bottom = y4;

16

Game_Main( )// make the blitter call

if (FAILED(lpddsprimary->Blt(&dest_rect, // pointer to dest RECT

lpddsback, // source surface

&source_rect,// pointer source RECT

DDBLT_WAIT, // control flags

NULL))) // pointer to DDBLTFX info

return(0);

17

Clipping

• Suppose you want to clip a pixel with coordinates (x,y) to a viewport from (x1,y1) to (x2,y2)

void Plot_Pixel_Clip8(int x, int y,

UCHAR color, UCHAR *video_buffer)

{

// test for coordinates in range

if (x >= x1 && x <<= x2 && y >= y && y <= y2)

video_buffer[x + y * 640] = color;

}

18

Clipping Bitmaps the Hardway

• Method 1 (image space clipping)– Clip each pixel of the bitmap individually as

each is generated (simple but slow)

• Method 2 (object space clipping)– Clip the bounding rectangle of the bitmap

to the viewport and then only draw the pixels that are in range (complex, but very fast)

19

Blit_Clipped( )

void Blit_Clipped(int x, int y, // position to draw bitmap

int width, int height, // size of bitmap in pixels

UCHAR *bitmap, // pointer to bitmap data

UCHAR *video_buffer, // pointer to video buffer surface

int mempitch) // video pitch per line

{

// this function blits and clips the image sent in bitmap to the

// destination surface pointed to by video_buffer

// the function assumes a 640x480x8 mode

 

// first do trivial rejections of bitmap, is it totally invisible?

if ((x >= SCREEN_WIDTH) || (y>= SCREEN_HEIGHT) ||

((x + width) <= 0) || ((y + height) <= 0))

return;

20

Blit_Clipped( )// clip source rectangle// pre-compute the bounding rect to make life easyint x1 = x;int y1 = y;int x2 = x1 + width - 1;int y2 = y1 + height -1; // upper left hand corner firstif (x1 < 0) x1 = 0;if (y1 < 0) y1 = 0; // now lower left hand cornerif (x2 >= SCREEN_WIDTH) x2 = SCREEN_WIDTH-1;if (y2 >= SCREEN_HEIGHT) y2 = SCREEN_HEIGHT-1;

21

Blit_Clipped( )// now we know to draw only the portions of the bitmap

// from (x1,y1) to (x2,y2)

// compute offsets into bitmap on x,y axes, we need this

// to compute starting point

// to rasterize from

int x_off = x1 - x;

int y_off = y1 - y;

 

// compute number of columns and rows to blit

int dx = x2 - x1 + 1;

int dy = y2 - y1 + 1;

 

// compute starting address in video_buffer

video_buffer += (x1 + y1*mempitch);

 

// compute starting address in bitmap to scan data from

bitmap += (x_off + y_off*width);

22

Blit_Clipped( ) // bitmap is pointing to the first pixel in bitmap that needs to // be blitted, and video_buffer is pointing to memory location on // the destination buffer to put it, so now enter rasterizer loop UCHAR pixel; // used to read/write pixels for (int index_y = 0; index_y < dy; index_y++) { // inner loop, where the action takes place for (int index_x = 0; index_x < dx; index_x++) { // read pixel from source bitmap, test transparency and plot if ((pixel = bitmap[index_x])) video_buffer[index_x] = pixel; } // end for index_x // advance pointers video_buffer += mempitch; // bytes per scanline bitmap += width; // bytes per bitmap row } // end for index_y

} // end Blit_Clipped

23

Creating Happy Face// the happy face structuretypedef struct HAPPY_FACE_TYP{ int x,y; // position of happy face int xv, yv; // velocity of happy face} HAPPY_FACE, *HAPPY_FACE_PTR;// a low tech bitmap that uses palette entry 1 for the color :)UCHAR happy_bitmap[64] = {0,0,0,0,0,0,0,0, 0,0,1,1,1,1,0,0, 0,1,0,1,1,0,1,0, 0,1,1,1,1,1,1,0, 0,1,0,1,1,0,1,0, 0,1,1,0,0,1,1,0, 0,0,1,1,1,1,0,0, 0,0,0,0,0,0,0,0};

HAPPY_FACE happy_faces[100]; // this holds all the happy faces

24

Game_Init( )// create IDirectDraw interface 7.0 object and test for error// set cooperation to full screen// set display mode // clear ddsd and set size// set the backbuffer count field to 1// create the primary surface (complex and flipable)// now query for attached surface from the primary surface// get the attached back buffer surface// build up the palette data array and create the palette object // finally attach the palette to the primary surface // initialize all the happy facesfor (int face = 0; face < 100; face++){ happy_faces[face].x = rand()%SCREEN_WIDTH; happy_faces[face].y = rand()%SCREEN_HEIGHT; happy_faces[face].xv = -2 + rand()%5; happy_faces[face].yv = -2 + rand()%5;} // end for face

25

Game_Main( )DDBLTFX ddbltfx; // the blitter fx structure // now set the color word info to the color we desireddbltfx.dwFillColor = 0; // make the blitter callif (FAILED(lpddsback->Blt(NULL, // pointer dest RECT NULL, // pointer to source surface NULL, // pointer to source RECT DDBLT_COLORFILL | DDBLT_WAIT, // do a color fill and wait if you have to &ddbltfx))) // pointer to DDBLTFX info return(0); // initialize ddsdDDRAW_INIT_STRUCT(ddsd);// lock the back buffer surfaceif (FAILED(lpddsback->Lock(NULL,&ddsd, DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR, NULL))) return(0);

26

Game_Main( )// draw all the happy faces

for (int face=0; face < 100; face++)

{

Blit_Clipped(happy_faces[face].x,

happy_faces[face].y,

8,8,

happy_bitmap,

(UCHAR *)ddsd.lpSurface,

ddsd.lPitch);

} // end for

27

Game_Main( )// move all happy facesfor (face=0; face < 100; face++){ // move happy_faces[face].x+=happy_faces[face].xv; happy_faces[face].y+=happy_faces[face].yv; // check for off screen, if so wrap if (happy_faces[face].x > SCREEN_WIDTH) happy_faces[face].x = -8; else if (happy_faces[face].x < -8) happy_faces[face].x = SCREEN_WIDTH;  if (happy_faces[face].y > SCREEN_HEIGHT) happy_faces[face].y = -8; else if (happy_faces[face].y < -8) happy_faces[face].y = SCREEN_HEIGHT;} // end face

28

DirectDraw Clipping

• Create a DirectDraw clipper object

• Create a clipping list

• Send clipping list to clipper using SetClipList( )

• Attach clipper to window and/or surface using SetClipper( )

29

Creating Clipper ObjectLPDIRECTDRAWCLIPPER lpddclipper; // pointer to the

// newly created dd clipper

LPRGNDATA region_data; // pointer to the region

// data that contains

// the header and clip list

 

// first create the direct draw clipper

if (FAILED(lpdd->CreateClipper(0,&lpddclipper,NULL)))

return(NULL);

30

Creating Clipping List// first allocate memory for region dataregion_data = (LPRGNDATA)malloc(sizeof(RGNDATAHEADER)

+num_rects*sizeof(RECT)); // now copy the rects into region datamemcpy(region_data->Buffer, clip_list, sizeof(RECT)*num_rects); // set up fields of headerregion_data->rdh.dwSize = sizeof(RGNDATAHEADER);region_data->rdh.iType = RDH_RECTANGLES;region_data->rdh.nCount = num_rects;region_data->rdh.nRgnSize = num_rects*sizeof(RECT); region_data->rdh.rcBound.left = 64000;region_data->rdh.rcBound.top = 64000;region_data->rdh.rcBound.right = -64000;region_data->rdh.rcBound.bottom = -64000;

31

Creating Clipping List

// find bounds of all clipping regions

for (index=0; index<num_rects; index++)

{

// test if the next rectangle unioned with current bound is larger

if (clip_list[index].left < region_data->rdh.rcBound.left)

region_data->rdh.rcBound.left = clip_list[index].left;

 

if (clip_list[index].right > region_data->rdh.rcBound.right)

region_data->rdh.rcBound.right = clip_list[index].right;

 

if (clip_list[index].top < region_data->rdh.rcBound.top)

region_data->rdh.rcBound.top = clip_list[index].top;

if (clip_list[index].bottom > region_data->rdh.rcBound.bottom)

region_data->rdh.rcBound.bottom = clip_list[index].bottom;

} // end for index

32

Set the Clipping List

// we have computed the bounding rectangle region and set up the data

// now let's set the clipping list

if (FAILED(lpddclipper->SetClipList(region_data, 0)))

{

// release memory and return error

free(region_data);

return(NULL);

} // end if 

33

Attach Clipper and Clean Up

// we have computed the bounding rectangle region and set up the data

// now attach the clipper to the surface

if (FAILED(lpdds->SetClipper(lpddclipper)))

{

// release memory and return error

free(region_data);

return(NULL);

} // end if

 

// all is well, so release memory and send back the pointer to

// the new clipper

free(region_data);