YangW

无为,无我,无欲,居下,清虚,自然

通过直接操作显存实现高速绘图和特殊效果(如逐渐变亮)

[开场白]

EasyX 从 2011-2-24 开始,支持了直接操作显存的功能。当然,这个显存并不是通常所说的显卡上的显存,而是用来显示窗口的内存,以及用来保存 IMAGE 对象的内存。为了有别于普通的内存,暂且叫这部分为显存吧。

传统的显存概念这里就不介绍了,这里只说一下在 EasyX 中怎么利用这个“显存”。

比如画点,传统的画点函数要考虑很多因素,比如坐标系、缩放、裁剪区等等,所以性能要差。要实现高速绘图,就需要一些更直接的方法,这就是 EasyX 实现直接操作显存功能的目的。

[理论知识]

在 EasyX 中,每个点用 4 个字节表示。对于 640 x 480 的绘图窗口,显存的大小为 640 x 480 x 4 = 307,200 x 4 = 1,228,800 字节。
一个 DWORD 类型正好四个字节,如果是 DWORD 类型的指针指向显存,那么用数组的形式,数组的下标范围是 0~307,199。

关于颜色有点需要注意的地方。平时我们用 COLORREF 表示颜色,但是显存中的颜色是 RGBTRIPLE 结构体格式的(该结构体的定义请参考 MSDN)。简单来说,RGBTRIPLE 在内存中的表示形式为:0xrrggbb (bb=蓝,gg=绿,rr=红),而 COLORREF 在内存中的表示形式为:0xbbggrr。所以,直接操作显存时,需要交换红色和蓝色。

以上内容为理论部分,在进行 Windows 编程的时是一样的。

[使用方法]

EasyX 中使用 GetImageBuffer(IMAGE* pImg) 函数获取绘图窗口或者 IMAGE 对象的显存的指针。如果 pImg 为 NULL,就获取绘图窗口的指针。
还可以使用宏 BGR 交换红色和蓝色。

以下示例用一个 DWORD 指针指向显存,并将显存中填满蓝色:

// 编译环境:Visual C++ 6.0, EasyX 2011惊蛰版
//
#include <graphics.h>
#include <conio.h>

void main()
{
	// 初始化绘图窗口
	initgraph(640, 480);

	// 获取绘图窗口的显存指针
	DWORD* p = GetImageBuffer();

	// 对显存中的每个点赋值
	for(int i = 0; i < 640 * 480; i++)
		p[i] = BGR(BLUE);

	// 使显存操作生效(注:操作指向 IMAGE 的显存时不需要这条语句)
	FlushBatchDraw();

	// 按任意键退出
	getch();
	closegraph();
}

下面这个例子,可以实现逐渐变亮的效果(注意,请确保 D:\test.jpg 的大小是 640x480):

// 编译环境:Visual C++ 6.0, EasyX 2011惊蛰版
//
#include <graphics.h>
#include <conio.h>

void main()
{
	// 初始化绘图窗口
	initgraph(640, 480);

	// 定义并加载图片
	IMAGE img;
	loadimage(&img, "D:\\test.jpg");	// 请确保该图片是 640x480 像素

	// 获取绘图窗口和 img 对象的显存指针
	DWORD* pbWnd = GetImageBuffer();
	DWORD* pbImg = GetImageBuffer(&img);

	// 计算原图片每个点的颜色,实现逐渐变量的效果
	int r, g, b;
	for(int light = 1; light <= 64; light++)
	{
		for(int i = 0; i < 640 * 480; i++)
		{
			r = (int)(GetRValue(pbImg[i]) * light >> 6);
			g = (int)(GetGValue(pbImg[i]) * light >> 6);
			b = (int)(GetBValue(pbImg[i]) * light >> 6);
			pbWnd[i] = RGB(r, g, b);
		}

		// 使之前的显存操作生效(注:操作指向 IMAGE 的显存时不需要这条语句)
		FlushBatchDraw();
		Sleep(10);
	}

	// 按任意键退出
	getch();
	closegraph();
}

希望此文能起到抛砖引玉的作用,相信大家可以做出更绚丽的效果。

分享到

添加评论