0
点赞
收藏
分享

微信扫一扫

EGE基础入门篇(八):清屏与重绘

1kesou 2022-03-15 阅读 70
c++ege

EGE专栏:EGE专栏

上一篇:EGE基础入门篇(七):组合图形

下一篇:EGE基础入门篇(九):双缓冲与手动渲染


一、清屏

1. 控制台清屏

  当控制台输出的字符内容达到一定行数的时候,控制台会自动向下滚动,以便用户能查看到最新的输出。并且用户可拖动滚动条查看之前输出的信息,便于查看历史记录
  但如果是用控制台做UI界面的话,太多的信息显示在窗口中反而显得杂乱,并且很难一眼看到关键信息。如下图所示:

在这里插入图片描述

  如果在输出前,先调用cls命令清屏,将之前的输出信息清除,再输出当前要显示的文字,那么窗口上只显示相关内容,清晰明了,有利于用户的阅读和界面交互。如下图所示:

在这里插入图片描述
  但是清屏会使得控制台之前的输出信息会丢失,所以清屏并在输出调试信息时使用。

  控制台可以控制光标位置,当需要动态输出时,比如加载进度的显示,可以稍微跳转到目标位置,改变字符,这适用于明确输出内容并且只有少量修改的情况。在不清楚之前控制台中的输出内容,以及需要大量改变输出的时候,还是需要进行清屏,然后重新打印输出。

1.1 控制台清屏命令:cls

  在程序中包含 <stdlib.h> 头文件:

#include <stdlib.h>

  当需要对控制台清屏时,调用 system("cls") 即可。

控制台清屏示例:
  下面是纯控制台的一个程序,在输出N行信息后,通过调用 system("cls") 将控制台清屏。

#include <stdlib.h>
#include <stdio.h>
#include <conio.h>

int main()
{
	for (int i = 0; i < 30; i++) {
		printf("[%2d]输出信息\n", i);
	}
	
	printf("\n按任意键将清除所有输出...\n");
	getch();
	
	system("cls");		//清屏
	
	printf("清除完毕,按任意键退出");

	getch();
	return 0;
}

2. 图形窗口清屏

2.1 像素颜色的表示

  一个像素的颜色可以用不同的位数来表示,如果只用一位(1 Bit)来表示,那么颜色值只有0和1,通常0表示黑,而1表示白,也可以根据实际,设置不同的颜色。

  现在的显示器基本都用RGB来表示颜色,每一个像素点由红绿蓝三原色的子像素点组成,用来三原色中的每种颜色亮度的位数称为位深度。显示器的位深度通常为6位,8位或10位,可以由显卡驱动在支持的最大位深度和较小位深度之间调节。大多数显示器支持的最大位深度位为8位,达到8位后大多数人已难以分辨,
  如果位深度为6,那么每种原色可以有 2 6 = 64 2^{6} =64 26=64种亮度,三种原色组合则一共可以表示 6 4 3 = 262144 64^{3}=262144 643=262144,约26万种颜色。
  如果位深度为8,那么每种原色可以有 2 8 = 256 2^{8} =256 28=256种亮度,三种原色组合则一共可以表示 25 6 3 = 16777216 256^{3}=16777216 2563=16777216,约1678万种颜色。
  在EGE中,像素颜色是ARGB格式,用4个字节来表示。每种原色用一个字节,可以表示256种亮度,一共可以表示16777216种颜色。当然,最终能够显示的颜色数,还是要看显示器能否支持。

2.1 窗口像素颜色值的存储:帧缓冲

  窗口区域通常是矩形,对于宽高分别为 w i d t h width width, h e i g h t height height 的矩形区域,如果表示一个像素颜色值需要 N N N个字节,可以开辟一个大小为 N ⋅ w i d t h ⋅ h e i g h t N \cdot width \cdot height Nwidthheight 字节的存储空间来表示窗口区域的图像内容。这个存储区域称之为帧缓冲(Frame Buffer),窗口的帧缓冲通常是在开辟在内存中,帧缓冲数据最终需要传输到显存中,由显卡读取并显示到屏幕上。
在这里插入图片描述
  在EGE中,像素颜色格式为ARGB格式,每一个像素点的颜色都用4个字节来表示。

2.2 窗口清屏的含义

  屏幕中每一个像素都需要有一个颜色值来与之对应,如果RGB颜色值为0,那么像素颜色对应的是纯黑。
  正因为每个像素都有一个颜色值与之对应,所以清屏时,不是将帧缓冲区删除,而是对帧缓冲里的每一个元素进行赋值,使整个帧缓冲里的每一个元素都是同一个值(可以是任意的颜色值) 。这样窗口里显示的就都是同一个颜色了,从而完成对图形的清除。
在这里插入图片描述

2.3 cleardevice()函数

  EGE中的 cleardevice() 函数可以将窗口帧缓冲数据统一设置成同一个颜色值,即背景色。背景色可以由 setbkcolor()setbkcolor_f() 进行设置。

setbkcolor_f(WHITE);
cleardevice();

清屏示例:

#include <graphics.h>

int main()
{
	initgraph(640, 480, INIT_RENDERMANUAL);

	setbkcolor(WHITE);					//设置背景色并修改背景	
	
	setfillcolor(EGERGB(0, 163, 254));
	bar(40, 40, 600, 440);

	getch();							//暂停,按任意键继续

	cleardevice();						//清屏

	getch();

	setfillcolor(EGERGB(249, 186, 0));
	bar(80, 80, 560, 400);

	getch();							//暂停,按任意键继续

	closegraph();

	return 0;
}

2.4 全部清屏和部分清屏

  全部清屏是将整个窗口的帧缓冲数据都赋同一个颜色值,这个操作实际上和绘制一个不透明的填充矩形是差不多的。cleardevice() 函数的功能便是全部清屏。
  当仅有一小部分需要修改的时候,我们并不需要将整个窗口清屏,只对部分区域清屏能提高绘图效率。因此当清楚绘图内容时,如果只需要修改一小部分内容,可以只对某一部分区域进行清屏,而不需要全部清屏。此时可以用填充矩形 bar() 将某一矩形区域填充成背景色,或者直接对帧缓冲进行赋值等均可。
  部分清屏用于绘图复杂时,能节省绘图时间,提高帧率。如果只是简单地绘图,全部清屏和部分清屏差别不大。

2.5 绘制新图形是否一定需要清屏

  清屏只是为了消除之前的绘图痕迹,保证后面图形能够正常绘制。如果直接绘图能够完全将之前的绘图痕迹覆盖,那就不是必要的。但为了稳妥起见,绘图前还是需要清屏的,这避免了许多意想不到的情况发生。
  现在的计算机性能很高,清屏并不是主要耗时原因。如果是比较低性能的嵌入式芯片,那就需要根据实际情况斟酌一番,性能差时
能省则省。

二、重绘

1. 为何需要清屏重绘

  如下图所示,中间小球在其它图形后面,部分被遮挡。现在蓝色小球要从左边移动到右边,该如何处理才能达到所要求的效果呢? 这里有两个要求:消除之前绘制小球时留下的痕迹,新绘制的小球需要保持原来的深度,即遮挡关系与原来一致。
在这里插入图片描述
  清屏重绘是一种简单而直接的处理方式。先将外围矩形区域内的所有图形数据清除,再按原来的绘制顺序进行绘制即可,改变的也仅仅是小球时绘制时的位置。

  如何想要不清屏而单纯地改变对应位置的像素,那是十分复杂的。首先需要对所有图形进行深度排序,消除痕迹需要了解绘图之后是否被其它图形遮挡,遮挡了哪些像素,并且需要存储图形的每个像素在绘图之前的值,如果图形都是透明的,那基本是要按照图形顺序重新算一遍。而在新位置绘制时,又要考虑图形之前的遮挡,当图形有透明度时,那必须按照原顺序重新计算,十分复杂。

2. 图形的重绘

  如果程序的图形界面是动态的,需要保证程序能有对图形进行重绘的能力,不管区域中原来绘制有什么图形,都能够绘制出目标图形,这通常需要先进行清屏,将在区域内的图形全部清空,再重新绘制。

#include <graphics.h>

//圆
typedef struct Circle
{
	float x, y;		//圆心坐标
	float radius;	//半径
} Circle;

int main()
{
	timeBeginPeriod(2);
	int winWidth = 640, winHeight = 480;

	initgraph(winWidth, winHeight, INIT_RENDERMANUAL | INIT_NOFORCEEXIT);

	setbkcolor(WHITE);					//设置背景色并修改背景	
	ege_enable_aa(true);

	int margin = 20;
	float rightBoundary = winWidth - margin, leftBoundary = margin;

	//移动速度(像素每帧)
	const float absSpeed = 4.0f;
	float speed = absSpeed;
	Circle cir = { margin + 100, winHeight / 2, 100 };
	
	color_t yellowColor = EGEARGB(255, 255, 217, 102);
	color_t pinkColor = EGEARGB(120, 193, 102, 89);
	color_t blueColor = EGEARGB(255, 60, 120, 216);

	setlinewidth(2);

	bool first = true;

	for (; is_run(); delay_fps(60)) {
		//清屏
		cleardevice();

		//绘制小球
		setfillcolor(blueColor);
		ege_fillellipse(cir.x - cir.radius, cir.y - cir.radius, 2 * cir.radius, 2 * cir.radius);

		//绘制顶部矩形
		setfillcolor(yellowColor);
		setcolor(BLACK);
		ege_fillrect(margin, winHeight / 2 - 100, winWidth - 2 * margin, 40);
		ege_rectangle(margin, winHeight / 2 - 100, winWidth - 2 * margin, 40);

		//绘制顶部矩形
		setfillcolor(pinkColor);
		setcolor(BLACK);
		ege_fillrect(margin, winHeight / 2 + 100 - 40, winWidth - 2 * margin, 40);
		ege_rectangle(margin, winHeight / 2 + 100 - 40, winWidth - 2 * margin, 40);

		//移动小球
		cir.x += speed;

		//边界碰撞判断
		if (cir.x + cir.radius > rightBoundary) {
			cir.x = rightBoundary - cir.radius;
			speed = -absSpeed;
		}
		else if (cir.x - cir.radius < leftBoundary) {
			cir.x = leftBoundary + cir.radius;
			speed = absSpeed;
		}
	}
	
	timeEndPeriod(2);

	closegraph();

	return 0;
}


EGE专栏:EGE专栏

上一篇:EGE基础入门篇(七):组合图形

下一篇:EGE基础入门篇(九):双缓冲与手动渲染

举报

相关推荐

0 条评论