Blitter Fill
bit blitter
is used to copy bitmap images from some source to a destination (usually the
primary surface). It can also be used to clear or fill an area.
Blt(<destRecPtr>, <sourceSurfPtr>, <sourceRecPtr>,
<flags>,
<FXptr>); // w/clipping
· returns DD_OK if success
· dest surface defined by call
lpDest->Blit();
· if destRecPtr == null entire
destination surface is used
· if dest != source size,
image is scaled to fit
· if sourceRecPtr == null the
entire surface is used
· FXptr points to more info on
how to operate
· since done in video
hardware, CPU goes on in parallel
flags:
|
DDBLT_COLORFILL |
dwFillColor member of DDB LTFX struct
has RGB fill color |
|
_DDFX |
dwDDFX
specifies effects |
|
_DDROPS |
dwDROPS
specifies raster ops |
|
_KEYDEST |
uses
destination’s color key |
|
_KEYSRC |
uses
source’s color key |
|
_ASYNC |
Perform
blit async (faster) |
|
_ROP |
uses
Win32 raster ops |
|
_ROTATIONANGLE |
dwRotationAngle
holds rotation angle (in 1/100 of a degree) |
|
_WAIT |
keep
trying until success |
typedef
struct _DDBLITFX
{
DWORD dwSize; // struct size
DWORD dwDDFX;
// type of blitter
DWORD dwROP;
// Win32 raster ops supported
DWORD dwDROP;
// DD raster ops supported
DWORD dwRotationAngle;
// rot angle
DWORD dwZBufferOpCode; // z buff field for
3D
// … unimportant junk
union
{ DWORD dwFillColor;
// color for fill
DWORD dwFillDepth;// z filling (advanced)
DWORD dwFillPixel; // color for
RGB alpha fills
}
LPDIRECTDRAWSURFACE lpDDSPattern;
DDCOLORKEY ddckDestColorkey; // dest color key
DDCOLORKEY ddchSrcColorkey; // source key
}
DDBLTFX, FAR* LPDDBLTFX;
example:
// color an area of the screen
//
load fill color in dwFillColor
//
define a RECT to blit to
//
call Blt() from the dest surface
DDBLTFX
ddbltfx; // parms structure
RECT
fillArea; // rectangle to fill
memset(&ddbltfx,
0, sizeof(DDBLTFX)); // clear
ddbltfx.dwSize
= sizeof(DDBLTFX);
ddbltfx.dwFillColor
= _RGB16BIT565(75, 0, 75); // 16 bit
with
fillArea // define area to fill
{ .top = 10; .left = 10; .bottom = 100; .right
= 100;}
lpddprimary->Blt(&fillArea,
NULL, NULL,
DDBLT_COLORFILL | CCBLTWAIT,
&ddbltfx);
Fast blit() -- no clipping
HRESULT
BltFast(<dest x>, <dest
y>, <ptr to source surface>, <ptr to source RECT>,
<type>);
(x,
y) – where to put on dest
flags:
|
DDBLTFAST_SRCCOLORKEY |
Transparent
blit using source color key |
|
_DESTCOLORKEY |
Transparent
blit using destination color key |
|
_NOCOLORKEY |
Normal
blit, no transparency |
|
_WAIT |
keep
trying until success |
Example:
//
surface to surface blitter 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.h> // 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 16 // 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)
// this
builds a 16 bit color value in 5.5.5 format (1-bit alpha mode)
#define
_RGB16BIT555(r,g,b) ((b & 31) + ((g & 31) << 5) + ((r & 31)
<< 10))
// this
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))
// this
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))
//
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; // dd4 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
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
RECT
source_rect, // used to hold the destination RECT
dest_rect;
// used to hold the destination RECT
// 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
// get a
random rectangle for source
int x1 =
rand()%SCREEN_WIDTH;
int y1 =
rand()%SCREEN_HEIGHT;
int x2 =
rand()%SCREEN_WIDTH;
int y2 =
rand()%SCREEN_HEIGHT;
// get a
random rectangle for destination
int x3 =
rand()%SCREEN_WIDTH;
int y3 =
rand()%SCREEN_HEIGHT;
int x4 =
rand()%SCREEN_WIDTH;
int y4 =
rand()%SCREEN_HEIGHT;
// now
set up the RECT structure to fill the region from
//
(x1,y1) to (x2,y2) on the source surface
source_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 surface
dest_rect.left = x3;
dest_rect.top = y3;
dest_rect.right = x4;
dest_rect.bottom
= y4;
// make
the blitter call
if
(FAILED(lpddsprimary->Blt(&dest_rect,
// pointer to dest RECT
lpddsback, // pointer to source surface
&source_rect,// pointer to source RECT
DDBLT_WAIT, // control flags
NULL))) // pointer to DDBLTFX holding info
return(0);
//
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
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 | DDSD_BACKBUFFERCOUNT;
// set
the backbuffer count field to 1, use 2 for triple 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);
// draw
a green gradient in back buffer
DDRAW_INIT_STRUCT(ddsd);
// lock
the back buffer
if
(FAILED(lpddsback->Lock(NULL,&ddsd, DDLOCK_SURFACEMEMORYPTR |
DDLOCK_WAIT,NULL)))
return(0);
// get
alias to start of surface memory for fast addressing
USHORT
*video_buffer = (USHORT *)ddsd.lpSurface;
// draw
the gradient
for (int
index_y=0; index_y < SCREEN_HEIGHT; index_y++)
{
// build color word up
DWORD color = _RGB16BIT565(0,(index_y
>> 3),0);
// replicate color in upper and lower 16
bits of 32-bit word
color = (color) | (color << 16);
// now color has two pixel in it in 16.16
or RGB.RGB format, use a DWORD
// or 32-bit copy to move the bytes into
the next video line, we'll need
// inline assembly though...
// draw next line, use a little inline asm
baby!
_asm
{
CLD ; clear direction of
copy to forward
MOV EAX, color ; color goes here
MOV ECX, (SCREEN_WIDTH/2) ; number of DWORDS goes here
MOV EDI, video_buffer ; address of line to move data
REP STOSD ; send the pentium X on its
way
} // end asm
// now advance video_buffer to next line
video_buffer += (ddsd.lPitch >> 1);
} // end for index_y
//
unlock the back buffer
if
(FAILED(lpddsback->Unlock(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
// now
the back buffer surface
if
(lpddsback)
{
lpddsback->Release();
lpddsback = 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
//
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 = 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
WINDOW_CLASS_NAME, // class
"DirectDraw Blitter Filling
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
///////////////////////////////////////////////////////////
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
{
// 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 with linear pitch
// first
do trivial rejections of bitmap, is it totally invisible?
if ((x
>= SCREEN_WIDTH) || (y>= SCREEN_HEIGHT) ||
((x + width) <= 0) || ((y + height)
<= 0))
return;
// clip
source rectangle
//
pre-compute the bounding rect to make life easy
int x1 =
x;
int y1 =
y;
int x2 =
x1 + width - 1;
int y2 =
y1 + height -1;
// upper
left hand corner first
if (x1
< 0)
x1 = 0;
if (y1
< 0)
y1 = 0;
// now
lower left hand corner
if (x2
>= SCREEN_WIDTH)
x2 = SCREEN_WIDTH-1;
if (y2
>= SCREEN_HEIGHT)
y2 = SCREEN_HEIGHT-1;
// 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*640);
//
compute starting address in bitmap to scan data from
bitmap
+= (x_off + y_off*width);
// at
this point bitmap is pointing to the first pixel in the bitmap that needs to
// be
blitted, and video_buffer is pointing to the 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 for transparency
if ((pixel = bitmap[index_x]))
video_buffer[index_x] = pixel;
} // end for index_x
// advance pointers
video_buffer+=640; // bytes per scanline
bitmap +=width; // bytes per bitmap row
} // end for index_y
} // end Blit_Clipped