Windows编程 32位色彩正式教学 在Direct中使用双缓冲、三缓冲(Triple Buffering)

版本:VS2015      语言:C++

 

现在估计很少有能使用32位色彩以下的显卡了吧(也没必要吧),所以我就把8位、16位、24位这样的内容跳过,直接上32位,而且所有的代码都是能在Win10 - VS2015中运行的。

 

因为是32位存储,所以调色板不需要了,那个是在8位中使用的。

 

好了,让我们来看一下初始化的代码:

// 游戏初始化
int Game_Init(void* params = NULL)
{
	// 基础设置
	if (FAILED(DirectDrawCreateEx(NULL, (void**)&lpdd, IID_IDirectDraw7, NULL)))	//获取d7对象
		return 0;
	if (FAILED(lpdd->SetCooperativeLevel(main_window_handle,
		//DDSCL_NORMAL
		DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX | DDSCL_EXCLUSIVE | DDSCL_ALLOWREBOOT
		)))	//跟windows协作等级设置为全屏,这是最常用的参数
		return 0;
	if (FAILED(lpdd->SetDisplayMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, 0, 0)))	//设置显示模式,如果设置为8位会直接出错
		return 0;

	// 开始创建显示主界面
	memset(&ddsd, 0, sizeof(ddsd));
	ddsd.dwSize = sizeof(ddsd);
	ddsd.dwFlags = DDSD_CAPS;	//表明ddsCaps是个有效成员
	ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;	//表明该界面是主界面

	lpdd->CreateSurface(&ddsd, &lpddsprimary, NULL);	//根据界面描述创建主界面

	return 1;
}


一样的,就是去掉了调色板,首先获取d7对象,然后创建主界面描述,最后根据描述创建主界面。

 

现在给大家带来最关心的如何使用32位绘图,这边我使用了两中方法,原理是一样的:

LPWSTR msg = new TCHAR[1024];
// 弹出消息
void popMessage(LPWSTR str)
{
	MessageBox(main_window_handle, str, TEXT("提示"), MB_OK);
}

// 32位像素上色
// 不理解的童鞋想一下:我们一般说的都是argb,内存中也是如此排列的,a是放在的是最高位,b是最低位,按照习惯从左往右读就是argb。
// 但是要记住char[0]到char[4]这样排列实际上是内存从小到大排列的,所以写的时候感觉是相反的
void Plot_Pixel_Fast32(int x, int y, int red, int green, int blue, int alpha, UCHAR* video_buffer, int lpitch)
{
	DWORD pixel_addr = 4 * x + y * lpitch;	//获取当前要写入的地址

	video_buffer[pixel_addr] = blue;	//写入颜色
	video_buffer[pixel_addr + 1] = green;
	video_buffer[pixel_addr + 2] = red;
	video_buffer[pixel_addr + 3] = alpha;
}

// 游戏主循环
int Game_Main(void* params = NULL)
{
	// 判断是否要退出
	if (KEYDOWN(VK_ESCAPE))
		PostMessage(main_window_handle, WM_CLOSE, 0, 0);

	// 初始化主界面描述
	DDRAW_INIT_STRUCT(ddsd);

	if (FAILED(lpddsprimary->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR, NULL)))	//加锁
	{
		wsprintf(msg, TEXT("LOCK 出错了"));
		popMessage(msg);
	}

	//画颜色
	UCHAR *video_buffer = (UCHAR*)ddsd.lpSurface;
	for (int x = 0; x < 640; ++x)
		for (int y = 0; y < 480; ++y)
			Plot_Pixel_Fast32(x, y, 0, 255, 0, 128, video_buffer, ddsd.lPitch);

	if (FAILED(lpddsprimary->Unlock(NULL)))	//解锁
	{
		wsprintf(msg, TEXT("UNLOCK 出错了"));
		popMessage(msg);
	}
	return 1;
}

是不是感觉比8位的还方便?直接在获取主界面缓存中写就行了,让我们再来简化简化,首先加入另一方法:

#define _RGB32BIT(a, r, g, b) ((b) + (g << 8) + (r << 16) + (a << 24))
// 32位像素上色
// 使用第二种方法
void Plot_Pixel_Fast32_2(int x, int y, int red, int green, int blue, int alpha, UINT* video_buffer, int lpitch)
{
	video_buffer[x + y * (lpitch>>2)] = (UINT)(_RGB32BIT(alpha, red, green, blue));	//使用宏直接写,有点区别的是lpitch需要除以4,因为lpitch算的是横向的字节数,而我们把主界面的内存弄成UINT型,是32位、4个字节的,上一节中是我理解的不够深刻
}

然后游戏循环中的画颜色步骤替换成一下代码:

UINT *video_buffer = (UINT*)ddsd.lpSurface;
	for (int x = 0; x < 640; ++x)
		for (int y = 0; y < 480; ++y)
			Plot_Pixel_Fast32_2(x, y, 255, 0, 0, 128, video_buffer, ddsd.lPitch);

注意这边用的是int了,最后来看看结果:



嗯,大红色(当然不是用画图工具画出来的!)。绘制的方式还是比较简单的,仔细分清各个步骤,不要写错就能出结果。

 

下面来看看界面的双重缓存。

 

现在游戏应该是3层缓存、或者可能更多吧,主要还是提高游戏运行的流畅度,玩家们想想看CPU为什么要加3级高速缓存呢?其实也就是这个道理吧。

 

让我们来看看代码:

// 游戏初始化
int Game_Init(void* params = NULL)
{
	// 基础设置
	if (FAILED(DirectDrawCreateEx(NULL, (void**)&lpdd, IID_IDirectDraw7, NULL)))	//获取d7对象
		return 0;
	if (FAILED(lpdd->SetCooperativeLevel(main_window_handle,
		//DDSCL_NORMAL
		DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX | DDSCL_EXCLUSIVE | DDSCL_ALLOWREBOOT
		)))	//跟windows协作等级设置为全屏,这是最常用的参数
		return 0;
	if (FAILED(lpdd->SetDisplayMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, 0, 0)))	//设置显示模式,如果设置为8位会直接出错
		return 0;

	// 开始创建显示主界面
	memset(&ddsd, 0, sizeof(ddsd));
	ddsd.dwSize = sizeof(ddsd);
	ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;	//表明ddsCaps是个有效成员,并且拥有后备的缓冲
	ddsd.dwBackBufferCount = 1;	//表明有一个缓冲
	ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | //表明该界面是主界面
			DDSCAPS_COMPLEX | //表明拥有缓冲链
			DDSCAPS_FLIP;	//表明是反正结构的一部分,上面的参数相当于是有缓冲,而这个参数表明可以切换缓冲

	lpdd->CreateSurface(&ddsd, &lpddsprimary, NULL);	//根据界面描述创建主界面

	// 开始创建后备界面,
	// 这边其实不算是创建,创建主界面的时候所有的后备表面已经创建完成,
	// 我们之后的工作只是把最后一个表面的指针取出来
	ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;	//表明该界面是后备界面
	if (FAILED(lpddsprimary->GetAttachedSurface(&ddsd.ddsCaps, &lpddsback)))	//通过主界面创建出备用表面
	{
		popMessage(TEXT("创建备用表面出错了"));
		return 0;
	}

	return 1;
}


// 游戏主循环
int Game_Main(void* params = NULL)
{
	// 判断是否要退出
	if (KEYDOWN(VK_ESCAPE))
		PostMessage(main_window_handle, WM_CLOSE, 0, 0);

	// 初始化主界面描述
	DDRAW_INIT_STRUCT(ddsd);

	if (FAILED(lpddsback->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR, NULL)))	//有备用表面时用备用表面加锁
	{
		wsprintf(msg, TEXT("LOCK 出错了"));
		popMessage(msg);
	}

	//画颜色
	UINT *video_buffer = (UINT*)ddsd.lpSurface;
	for (int x = 0; x < 640; ++x)
		for (int y = 0; y < 480; ++y)
			Plot_Pixel_Fast32_2(x, y, 0, 0, 255, 128, video_buffer, ddsd.lPitch);

	if (FAILED(lpddsback->Unlock(NULL)))	//解锁
	{
		wsprintf(msg, TEXT("UNLOCK 出错了"));
		popMessage(msg);
	}

	while (FAILED(lpddsprimary->Flip(NULL, DDFLIP_WAIT)));	//切换界面,这边的while不是很懂,应该每次只会调用一次

	return 1;
}

在创建主界面的时候设置后备的参数,然后再从主界面取出备用界面就OK了。在游戏循环中直接在备用界面中着色,完成后由主界面进行切换。

 

那么怎么创建3重缓冲呢?书上没有说,我一开始以为要创建两次,然后我就google了一下,在微软的官网的找到了这么一句话:

You do not need to keep track of all surfaces in a triple buffered flippingchain. The only surfaces you must keep pointers to are the primary surface andthe back-buffer surface.

 

意思就是不用显示调用循环链,直接放到备用缓冲里面就可以了,玩家们仔细看看我对后备缓冲创建时的注释。

 

嗯,这样就很简单了,直接把dwBackBufferCounting设置为2就行了,其他的操作跟双缓冲一样。

 

好了,这边的难度感觉确实要高点了,我看的有点慢,下一节还会讲第七章,是如何把图像拷贝到主界面中,按cocos里来说就是创建Sprite精灵。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值