Click here to Skip to main content
15,892,839 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Those days , I am reading the book 《Tricks of the Windows Game Programming》 . But I always get in trouble .
Today , I code the sample which I should use the Blt function . But I do not know , why the Blt function always goes wrong .

The following is my code which is wrong at the runtime ,
C++
// DEMO7_9.CPP 此Demo演示32位色彩模式下实现动画效果

// INCLUDES ///////////////////////////////////////////////

#define WIN32_LEAN_AND_MEAN  // just say no to MFC

#define INITGUID // make sure directX guids are included

#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
#pragma comment(lib,"ddraw.lib")
// DEFINES ////////////////////////////////////////////////

// defines for windows 
#define WINDOW_CLASS_NAME L"WINCLASS1"

// default screen size
#define SCREEN_WIDTH    1366  // size of screen
#define SCREEN_HEIGHT   768
#define SCREEN_BPP      32   // 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 DD_INIT_STRUCT(ddstruct) { memset(&ddstruct,0,sizeof(ddstruct)); ddstruct.dwSize=sizeof(ddstruct); }

//initializes a RGB value
#define _RGB16BIT565(r,g,b) ((b & 31) + ((g & 63) << 5) + ((r & 31) << 11))
#define _RGB32BIT(a,r,g,b) ((b) + ((g) << 8) + ((r) << 16) + ((a) << 24))

// GLOBALS ////////////////////////////////////////////////
HWND      main_window_handle = NULL; // globally track main window
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
LPDIRECTDRAWSURFACE7 lpddsOffScreen[8] ;     //离屏表面数组
int				  windows_closed = 0 ;
int					  index = 0 ;            //要加载的离屏表面的下标
RECT				  window_rect ;			  //保存窗口位置


// these defined the general clipping rectangle
int min_clip_x = 0,                          // clipping rectangle 
    max_clip_x = SCREEN_WIDTH-1,
    min_clip_y = 0,
    max_clip_y = SCREEN_HEIGHT-1;

// these are overwritten globally by DD_Init()
int screen_width  = SCREEN_WIDTH,            // width of screen
    screen_height = SCREEN_HEIGHT,           // height of screen
    screen_bpp    = SCREEN_BPP;              // bits per pixel


char buffer[80];                     // general printing buffer


//定义位图数据结构
typedef struct BITMAP_FILE_TAG
{
	BITMAPFILEHEADER bitmapfileheader ;
	BITMAPINFOHEADER bitmapinfoheader ;
	PALETTEENTRY palette[256] ;
	UCHAR * buffer ;
} BITMAP_FILE ,*BITMAP_FILE_PTR ;

//定义全局位图文件结构数组
BITMAP_FILE bitmap[8] ;

//申明位图加载函数
int Load_Bitmap_File(BITMAP_FILE_PTR pBitmap,WCHAR * pFileName);

//位图数据反转函数
int Flip_Bitmap_Data(UCHAR* pBitmap,int width , int height );

//位图卸载函数
int UnLoad_Bitmap_File(BITMAP_FILE_PTR pBitmap);

//Blt_Clipper方法,进行内存快移动并且进行裁剪
void Blt_Clipper(int x , int y,
				int width , int height , 
				UINT* bitmap_data,
				UINT* video_data,
				int mempitch);

//将24位位图数据转化为32位位图数据
void Transform24To32(UCHAR* pcBitmap ,int dwSizeImage ,UINT * pnBitmap );

//创建离屏表面
LPDIRECTDRAWSURFACE7 DDraw_Create_Surface(int width , int height , int mem_flags) ;

// 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
	
// for now test if user is hitting ESC and send WM_CLOSE
if(windows_closed)
	return 0 ;

if (KEYDOWN(VK_ESCAPE))
{
	PostMessage(main_window_handle,WM_CLOSE,0,0);
	windows_closed = 1 ;
}

//构造目标区域和源区域
GetWindowRect(main_window_handle,&window_rect);
RECT dest_rect ;
RECT src_rect ;
memset(&dest_rect,0,sizeof(RECT));
memset(&src_rect,0,sizeof(RECT));
dest_rect.left = 100 + window_rect.left;
dest_rect.top = 100 + window_rect.top;
dest_rect.right = dest_rect.left + bitmap[index].bitmapinfoheader.biWidth - 1 ;
dest_rect.bottom = dest_rect.top + bitmap[index].bitmapinfoheader.biHeight - 1 ;

src_rect.left = 0 ;
src_rect.top = 0 ;
src_rect.right = bitmap[index].bitmapinfoheader.biWidth - 1 ;
src_rect.bottom = bitmap[index].bitmapinfoheader.biHeight - 1 ;

//从离屏表面向表面Blt
if(FAILED(lpddsprimary->Blt(&dest_rect,lpddsOffScreen[index],&src_rect, DDBLT_KEYSRC|DDBLT_WAIT,NULL)))
{
	OutputDebugString(L"lpddsprimary->Blt error\n");
	return 0 ;
}

//进行index循环
index = (index+1)%8 ;
Sleep(300);

// 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 normal since this will be a windowed app
if(FAILED(lpdd->SetCooperativeLevel(main_window_handle,DDSCL_NORMAL)))
{
	MessageBox(NULL,L"SetCooperativeLevel error",L"error",MB_OK);
	return 0 ;
}

//创建主表面
memset(&ddsd,0,sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS ;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE ;

if(FAILED(lpdd->CreateSurface(&ddsd,&lpddsprimary,NULL)))
{
	MessageBox(NULL,L"CreateSurface error",L"error",MB_OK);
	return 0 ;
}

//调整窗口尺寸
/*RECT window_rect = {0,0,SCREEN_WIDTH*0.5,0.5*SCREEN_HEIGHT} ;
AdjustWindowRectEx(&window_rect,
	GetWindowStyle(main_window_handle),
	GetMenu(main_window_handle)!=NULL,
	GetWindowExStyle(main_window_handle));
MoveWindow(main_window_handle,CW_USEDEFAULT,CW_USEDEFAULT,window_rect.right-window_rect.left,
	window_rect.bottom-window_rect.top,
	FALSE);
ShowWindow(main_window_handle,SW_NORMAL);*/

//创建8个离屏表面
for(int i = 0 ; i < 6 ; i ++)
{
	lpddsOffScreen[i]=DDraw_Create_Surface(23,23,DDSCAPS_VIDEOMEMORY);
}

//加载8张位图
for(int i = 1 ; i <= 6 ; i ++)
{
	WCHAR * wcFileName = new WCHAR[256] ;
	swprintf_s(wcFileName,256,L"grossini_dance_0%d.bmp",i);
	Load_Bitmap_File(&bitmap[i-1],wcFileName);
	delete wcFileName ;
}

//将位图Blt到离屏表面
for(int i = 0 ; i < 6 ; i ++)
{
	DD_INIT_STRUCT(ddsd);
	if(FAILED(lpddsOffScreen[i]->Lock(NULL,&ddsd,DDLOCK_WAIT|DDLOCK_SURFACEMEMORYPTR,NULL)))
	{
		OutputDebugString(L"Lock error");
		return  0 ;
	}

	int mempitch = ddsd.lPitch>>2 ;
	UINT *video_buffer = (UINT*)ddsd.lpSurface ;
	UINT * bitmap_buffer = new UINT[bitmap[i].bitmapinfoheader.biSizeImage/3];
	Transform24To32(bitmap[i].buffer,bitmap[i].bitmapinfoheader.biSizeImage,bitmap_buffer);

	Blt_Clipper(0,0,bitmap[i].bitmapinfoheader.biWidth,bitmap[i].bitmapinfoheader.biHeight,
		bitmap_buffer,video_buffer,mempitch);

	//解锁
	if(FAILED(lpddsOffScreen[i]->Unlock(NULL)))
	{
		OutputDebugString(L"Unock error");
		return  0 ;
	}
}// end for 

// 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

// simply blow away the IDirectDraw4 interface
//卸载图像
for(int i = 0 ; i < 8 ; i ++)
{
	UnLoad_Bitmap_File(&bitmap[i]);
}

//释放离屏表面
for(int i = 0 ; i < 8 ; i ++)
{
	if(lpddsOffScreen[i] != NULL)
		lpddsOffScreen[i]->Release();
	lpddsOffScreen[i] = NULL ;
}

if(lpddsprimary)
{
	lpddsprimary->Release();
	lpddsprimary = NULL ;
}

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
								L"DirectDraw Initialization Demo", // title
								WS_OVERLAPPED | WS_VISIBLE,
					 			0,0,	  // initial x,y
								SCREEN_WIDTH*0.8,0.8*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

//位图加载函数
int Load_Bitmap_File(BITMAP_FILE_PTR  pBitmap , WCHAR * pFileName)
{
	//清空位图结构
	memset(pBitmap,0,sizeof(pBitmap));

	//打开文件
	HANDLE hFile = CreateFile(pFileName,GENERIC_READ,FILE_SHARE_READ,
		NULL,OPEN_EXISTING,FILE_ATTRIBUTE_READONLY,NULL);
	if(hFile == NULL)
	{
		OutputDebugString(L"CreateFile error");
		return 1 ;
	}

	SetFilePointer(hFile,0,NULL,FILE_BEGIN);

	//读取位图文件头
	DWORD readByte = 0 ;
	if(!ReadFile(hFile,(LPVOID)&pBitmap->bitmapfileheader,sizeof(BITMAPFILEHEADER),&readByte,NULL))
	{
		OutputDebugString(L"ReadFile for bitmapfileheader error");
		CloseHandle(hFile);
		return 1 ;
	}
	
	//判断文件是否为位图文件
	if(pBitmap->bitmapfileheader.bfType != 0x4D42)
	{
		OutputDebugString(L"file is not the bitmap");
		CloseHandle(hFile);
		return 1 ;
	}

	//读取位图信息头
	SetFilePointer(hFile,sizeof(BITMAPFILEHEADER),NULL,FILE_BEGIN);
	if(!ReadFile(hFile,(LPVOID)&pBitmap->bitmapinfoheader,sizeof(BITMAPINFOHEADER),&readByte,NULL))
	{
		OutputDebugString(L"ReadFile for bitmapinfoheader error");
		CloseHandle(hFile);
		return 1 ;
	}

	//判断是否有调色板
	if(pBitmap->bitmapinfoheader.biBitCount == 8)
	{
		SetFilePointer(hFile,sizeof(BITMAPINFOHEADER)+sizeof(BITMAPFILEHEADER),NULL,FILE_BEGIN);
		if(!ReadFile(hFile,(LPVOID)&pBitmap->palette,sizeof(PALETTEENTRY)*256,&readByte,NULL))
		{
			OutputDebugString(L"ReadFile for palette is error");
			CloseHandle(hFile);
			return 1 ;
		}

		//反转调色板信息
		for(int i = 0 ; i < 256 ; i ++)
		{
			int tempColor = pBitmap->palette[i].peRed ;
			pBitmap->palette[i].peRed = pBitmap->palette[i].peBlue ;
			pBitmap->palette[i].peBlue = tempColor ;
			pBitmap->palette[i].peFlags = PC_NOCOLLAPSE ;
		}
	}

	//读取位图数据
	if(pBitmap->bitmapinfoheader.biBitCount == 8 ||
		pBitmap->bitmapinfoheader.biBitCount == 16 ||
		pBitmap->bitmapinfoheader.biBitCount == 24 ||
		pBitmap->bitmapinfoheader.biBitCount == 32)
	{
		int length = 0 - pBitmap->bitmapinfoheader.biSizeImage ;
		SetFilePointer(hFile,length,NULL,FILE_END);
		
		//分配空间
		pBitmap->buffer = new UCHAR[pBitmap->bitmapinfoheader.biSizeImage];
		if(pBitmap->buffer == NULL)
		{
			OutputDebugString(L"new memory error");
			CloseHandle(hFile);
			return 1 ;
		}

		if(!ReadFile(hFile,(LPVOID)pBitmap->buffer,pBitmap->bitmapinfoheader.biSizeImage,&readByte,NULL))
		{
			OutputDebugString(L"ReadFile for buffer is error");
			CloseHandle(hFile);
			return 1 ;
		}
	}//end if
	else
	{
		OutputDebugString(L"the file is broken");
		CloseHandle(hFile);
		return 1 ;
	}// end else
	

	//关闭文件
	CloseHandle(hFile);

	//进行判断是否需要反转位图数据
	if(pBitmap->bitmapinfoheader.biHeight > 0)
	{
		Flip_Bitmap_Data(pBitmap->buffer,pBitmap->bitmapinfoheader.biWidth*pBitmap->bitmapinfoheader.biBitCount/8,
			pBitmap->bitmapinfoheader.biHeight);
	}

	return 0 ;
}// end Load_Bitmap_File

//定义翻转函数
int Flip_Bitmap_Data(UCHAR * pBitmap,int bytes_per_line , int height)
{

	// this function is used to flip bottom-up .BMP images

	UCHAR *buffer; // used to perform the image processing
	int index;     // looping index

	// allocate the temporary buffer
	if (!(buffer = (UCHAR *)malloc(bytes_per_line*height)))
	 return(0);

	// copy image to work area
	memcpy(buffer,pBitmap,bytes_per_line*height);

	// flip vertically
	for (index=0; index < height; index++)
	 memcpy(&pBitmap[((height-1) - index)*bytes_per_line],
			  &buffer[index*bytes_per_line], bytes_per_line);

	// release the memory
	free(buffer);

	// return success
	return(1);

}// end Flip_Bitmap_Data


//定义卸载位图函数
int UnLoad_Bitmap_File(BITMAP_FILE_PTR pBitmap)
{
	if(pBitmap->buffer != NULL)
	{
		delete pBitmap->buffer ;
		pBitmap->buffer = NULL ;
	}

	return  0 ;
}// end UnLoad_Bitmap_File

//定义Blt_Clipper方法,进行内存快移动并且进行裁剪
void Blt_Clipper(int x , int y,
				int width , int height , 
				UINT* bitmap_data,
				UINT* video_data,
				int mempitch)
{
	//判断位图是否可见
	if(x+width<0 || x>SCREEN_WIDTH 
		||y>SCREEN_HEIGHT ||y+height<0)
		return ;

	//确定在目标表面中绘制的区域
	int x1 = x ;
	int y1 = y ;
	int x2 = x + width - 1;
	int y2 = y + height - 1;

	if(x1<0)
		x1 = 0;
	if(y1<0)
		y1 = 0 ;
	if(x2>SCREEN_WIDTH)
		x2 = SCREEN_WIDTH - 1 ;  //注意这里需要减掉1,分辨率800*600的范围是(0,0)-(799,599)
	if(y2>SCREEN_HEIGHT)
		y2 = SCREEN_HEIGHT - 1 ;

	//求出将要画的第一个点在位图数据中的偏移
	int xoff = x1- x ;
	int yoff = y1 -y ;

	//求出将要画的位图的实际宽度和高度
	int dx = x2 - x1 + 1;  //注意这里的加1,4*4的位图,长度是(3-0+1,3-0+1),至于为什么是3上面解释过了
	int dy = y2 - y1 + 1;

	//计算目标区域的起点
	video_data += y1*mempitch+x1 ;

	//计算需要绘制的位图的起点
	bitmap_data+=yoff*width+xoff ;

	UINT piexl = 0 ;
	for(int y = 0 ; y < dy ; y ++)
	{
		for(int x = 0 ; x < dx ; x ++)
		{
			if(_RGB32BIT(0,255,255,255)!=(piexl=bitmap_data[x]))
				video_data[x] = piexl ;
		}

		//一轮循环之后在进行指针的修改
		bitmap_data += width ;
		video_data += mempitch ;
	}
}//end Blt_Clipper

//将24位位图数据转化为32位位图数据
void Transform24To32(UCHAR* pcBitmap ,int dwSizeImage ,UINT * pnBitmap )
{
	for(int i = 0 ,j = 0  ; i < dwSizeImage ; i += 3,j++)
	{
		UCHAR blue = pcBitmap[i+0] ,
			green = pcBitmap[i+1],
			red = pcBitmap[i+2];
		pnBitmap[j] = _RGB32BIT(0,red,green,blue);
	}
}//end Transform24To32

//创建离屏表面
LPDIRECTDRAWSURFACE7 DDraw_Create_Surface(int width , int height , int mem_flags)
{
	DDSURFACEDESC2 ddsd ;
	LPDIRECTDRAWSURFACE7 lpdds ;
	
	//初始化结构
	DD_INIT_STRUCT(ddsd);

	//设置表面描述符结构
	ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT ;
	ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | mem_flags ;
	ddsd.dwWidth = width ;
	ddsd.dwHeight = height ;

	//创建表面
	if(FAILED(lpdd->CreateSurface(&ddsd,&lpdds,NULL)))
	{
		OutputDebugString(L"CreateSurface for offscreen is error");
		return NULL ;
	}

	//这只源色键,即透明色
	DDCOLORKEY color_key ;
	color_key.dwColorSpaceHighValue = _RGB32BIT(0,255,255,255);
	color_key.dwColorSpaceLowValue = _RGB32BIT(0,255,255,255);
	lpdds->SetColorKey(DDCKEY_SRCBLT,&color_key);

	return lpdds ;
}//end DDraw_Create_Surface
//////////////////////////////////////////////////////////


The code above is just make an animation , but it does not work at all .

By the way , I am a Chinese , and I am useing the DirectX 9.0 to do this .
Is there anyone who can tell me the reason ? Thank you very much .
Or is there some function like GetLastError in WIN32 API that can tell me what 's wrong with the Blt ?
Please , help me . I really want to solve this problem .

When I debug this , and I get the result of the Blt function is E_INVALIDARG . But when I delete the DDBLT_KEYSRC , the function is successful . I do not understand it .
Posted
Updated 11-Jun-13 18:40pm
v3

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900