VC绘图/游戏简易教程--9:绘图中的位运算
作者:BestAns

教程总目录:http://www.easyx.cn/skills/View.aspx?id=45 (里面包括VC下的graphics.h的配置方法)

========================

位运算和绘图有什么关系?先举个例子来个感性认识:使用XOR运算可以实现擦除图形后不破坏背景,这在时钟程序中绘制表针是很有用的。稍后我们会给出这样的例子。

一、位运算的运算法则

位运算主要分4种:NOT、AND、OR、XOR,位运算的运算对象是二进制数(十进制要转换为二进制,计算机会自动转换)。

运算法则如下:

1. NOT
表示“取反”,将二进制位的1变0、0变1。
C语言用符号 ~ 表示。
如:
二进制: ~1101 = 0010
用十进制表示就是:~13 = 2

2. AND
表示“并且”,只有两数的对应二进制位都为1,结果的二进制位才为1;否则,结果的二进制位为0。
C语言用符号 & 表示。
如:
二进制:1101 & 0110 = 0100
用十进制表示就是:13 & 6 = 4

3. OR
表示“或者”,两数的对应二进制位只要有一个是1,结果的二进制位就是1;否则,结果的二进制位为0。
C语言用符号 | 表示。
如:
二进制:0101 | 0110 = 0111
用十进制表示就是:5 | 6 = 7

4. XOR
表示“异或”,两数的对应二进制位不同,结果的二进制位为1;相同,结果的二进制位为0。
C语言用符号 ^ 表示。
如:
二进制:0101 ^ 1110 = 1011

以上只是简单介绍一下,详细的还是请大家看课本上的讲解。

二、位运算的应用

位运算的应用很多,例如 AND 和 OR 在获取和设置标志位时经常使用。更多的,以后大家会逐渐遇到,暂时先记下有这么回事。

这里着重说一下 XOR 运算,它有一个重要的特性:(a ^ b) ^ b = a

也就是说,a ^ b 之后可能是某些其它数字,但是只要再 ^b 一下,就又成了 a。

一些简单的加密就用的 XOR 的这个特性。

至于绘图,假如 a 是背景图案,b 是将要绘制的图案,只要用 XOR 方式绘图,连续绘两次,那么背景是不变的。

三、演示

我们来一个简单的绘图 XOR 运算演示:

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

void main()
{
	initgraph(640, 480);				// 初始化 640 x 480 的绘图窗口
	setlinestyle(PS_SOLID, 10);			// 设置线宽为 10,这样效果明显
	setlinecolor(GREEN);				// 设置画线颜色为绿色
	rectangle(100, 100, 200, 200);		// 画一个矩形,当做背景图案

	setwritemode(R2_XORPEN);			// 设置 XOR 绘图模式
	setlinecolor(RED);					// 设置画线颜色为红色

	line(50, 0, 200, 300);				// 画线
	getch();							// 等待按任意键
	line(50, 0, 200, 300);				// 画线(XOR 方式重复画线会恢复背景图案)
	getch();							// 等待按任意键

	closegraph();						// 关闭绘图窗口
}

运行一下,看到第一次画线后,矩形与直线相交的部分,颜色变成了青色,青色就是白色和红色 XOR 的值。当再次以红色画线时,青色部分消失了,还原为完整的白色矩形框。

四、完整的范例

来一个相对完整的范例吧,就是钟表程序,三个表针用的都是 XOR 方式绘制,请大家运行体会一下 XOR 的作用:

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

#define PI 3.14159265359

void Draw(int hour, int minute, int second)
{
	double a_hour, a_min, a_sec;					// 时、分、秒针的弧度值
	int x_hour, y_hour, x_min, y_min, x_sec, y_sec;	// 时、分、秒针的末端位置
	
	// 计算时、分、秒针的弧度值
	a_sec = second * 2 * PI / 60;
	a_min = minute * 2 * PI / 60 + a_sec / 60;
	a_hour= hour * 2 * PI / 12 + a_min / 12;
	
	// 计算时、分、秒针的末端位置
	x_sec = 320 + (int)(120 * sin(a_sec));
	y_sec = 240 - (int)(120 * cos(a_sec));
	x_min = 320 + (int)(100 * sin(a_min));
	y_min = 240 - (int)(100 * cos(a_min));
	x_hour= 320 + (int)(70 * sin(a_hour));
	y_hour= 240 - (int)(70 * cos(a_hour));

	// 画时针
	setlinestyle(PS_SOLID, 10, NULL);
	setlinecolor(WHITE);
	line(320, 240, x_hour, y_hour);

	// 画分针
	setlinestyle(PS_SOLID, 6, NULL);
	setlinecolor(LIGHTGRAY);
	line(320, 240, x_min, y_min);

	// 画秒针
	setlinestyle(PS_SOLID, 2, NULL);
	setlinecolor(RED);
	line(320, 240, x_sec, y_sec);
}

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

	// 绘制一个简单的表盘
	circle(320, 240, 2);
	circle(320, 240, 60);
	circle(320, 240, 160);
	outtextxy(296, 310, _T("BestAns"));

	// 设置 XOR 绘图模式
	setwritemode(R2_XORPEN);	// 设置 XOR 绘图模式

	// 绘制表针
	SYSTEMTIME ti;				// 定义变量保存当前时间
	while(!kbhit())				// 按任意键退出钟表程序
	{
		GetLocalTime(&ti);		// 获取当前时间
		Draw(ti.wHour, ti.wMinute, ti.wSecond);	// 画表针
		Sleep(1000);							// 延时 1 秒
		Draw(ti.wHour, ti.wMinute, ti.wSecond);	// 擦表针(擦表针和画表针的过程是一样的)
	}

	closegraph();				// 关闭绘图窗口
}

五、作业

最后给出的绘制时钟的例子,很不完善,有不少问题。请完善该程序。例如样式上,表盘上没有刻度,没有数字,指针靠中心的一端应该长出来一点点,表盘太简单。还有就是尝试发现并改进功能实现上的问题。

更新时间:2010/5/17