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