慢羊羊的空间

工作做不完了,300出,无瑕。

快速画点的原理简述,以及写一个自己的快速画点函数 铜牌收录

EasyX 自带的 putpixel 函数源自 Windows GDI 函数 SetPixel,由于要考虑裁剪区、缩放、原点坐标、坐标方向等等诸多因素,所以性能很低,在一些只要求速度的场合很不实用。这篇文章就教你写一个自己的画点函数。

总的思想,是通过直接操作显示缓冲区来避免额外的运算。

在 EasyX 绘图窗口的显示缓冲区中,每个点占用 4 个字节,用 DWORD 指针指向显示缓冲区就可以像一维数组一样访问了。然后就是将二维坐标 (x, y) 映射到一维数组中,很简单,y * 640 + x 就是。另外需要注意的是,显示缓冲区中颜色的保存和 COLORREF 相比,蓝色和红色是相反的,需要用 BGR 宏转换一下(BGR 宏执行两次就会还原为原值)。

然后,我们整理出画点和读点的函数:
(记得要获取显示缓冲区指针并保存为全局变量)

DWORD* g_pBuf;

// 在 main 函数中
g_pBuf = GetImageBuffer();

void fast_putpixel(int x, int y, COLORREF c)
{
	g_pBuf[y * 640 + x] = BGR(c);
}

COLORREF fast_getpixel(int x, int y)
{
	COLORREF c = g_pBuf[y * 640 + x];
	return BGR(c);
}

作为完善,大家还可以自己加上判断数组是否越界、屏幕分辨率自适应等多种功能,这里不再详述。

 

最后看一个完整的范例,该范例在屏幕上随机画 100 个红点,然后用 fast_getpixel 扫描屏幕上每一个点,如果是红色,就以该点为圆心画一个圆:

#include <graphics.h>
#include <conio.h>

#define	WIDTH	640
#define	HEIGHT	480

// 显示缓冲区指针
DWORD* g_pBuf;

// 快速画点函数
void fast_putpixel(int x, int y, COLORREF c)
{
	g_pBuf[y * WIDTH + x] = BGR(c);
}

// 快速读点函数
COLORREF fast_getpixel(int x, int y)
{
	COLORREF c = g_pBuf[y * WIDTH + x];
	return BGR(c);
}

// 主函数
int main()
{
	// 初始化绘图窗口
	initgraph(WIDTH, HEIGHT);

	// 获取绘图窗口的显示缓冲区指针并保存为全局变量
	g_pBuf = GetImageBuffer();

	// 随机画 100 个点
	for(int i = 0; i< 100; i++)
		fast_putpixel(rand() % WIDTH, rand() % HEIGHT, RED);

	// 扫描每一个点,如果是红色,就以该点为圆心画一个圆:
	for(int x = 0; x < WIDTH; x++)
		for(int y = 0; y < HEIGHT; y++)
			if (fast_getpixel(x, y) == RED)
				circle(x, y, 10);

	// 按任意键退出
	_getch();
	closegraph();

	return 0;
}

评论 (2) -

  • 根据我已知的实验,该代码段 COLORREF c = g_pBuf[y * WIDTH + x];  计算的 (y * WIDTH + x) 值相对于绘图来讲,有一个隐藏的 BUG,就是当 x 的数值超过,窗口的 x 宽度时,再使用缓冲区绘图会使越过 x 界的坐标,转移到 x 的初始值,有点像贪吃蛇,穿过右方的屏幕头又会从左边出来,建议修改方案成:
    COLORREF c =0;
    if(x<WIDTH )c= g_pBuf[y * WIDTH + x];
    即可解决。
    • 是的,不仅 x 坐标会越界,y 坐标也会,看懂这个示例代码之后,如果有需要,可以加上自己需要的越界判断。

添加评论