EGE专栏:EGE专栏

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

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


一、清屏

  清屏清除屏幕(clear screen) ,可以将窗口上的输出内容全部清空,是重绘常用的一种方法。

1. 控制台清屏

  在控制台下,执行 cls命令 将清除控制台中的所有输入输出,光标回到左上角。

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

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


  但是清屏会使得控制台之前的输出信息会丢失,所以清屏并不适合在输出调试信息时使用。

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

1.1 控制台清屏命令:cls

控制台的 cls命令 可以通过 system() 函数调用, system() 函数在 <stdlib.h> 头文件中声明。

  在cpp源文件中包含 <stdlib.h> 头文件:

#include <stdlib.h>

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

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 窗口像素颜色值的存储:帧缓冲

  窗口区域通常是矩形,对于宽高分别为 widthwidthwidth, heightheightheight 的矩形区域,如果表示一个像素颜色值需要NNN个字节,那么开辟一个大小为 N⋅width⋅heightN \cdot width \cdot heightN⋅width⋅height 字节的存储空间就可以用来表示窗口区域的图像内容。这个存储空间称之为帧缓冲(Frame Buffer),窗口的帧缓冲通常是在开辟在内存中,帧缓冲数据最终需要传输到显存中,由显卡读取并显示到屏幕上。
  在EGE中,像素颜色格式为ARGB格式,每一个像素点的颜色都用4个字节来表示,这样可以表示约1678万种颜色。

2.1.1 获取帧缓冲首地址

  EGE中图像和窗口内容的帧缓冲首地址,可以使用 getbuffer() 函数获取。
  像素颜色的类型为color_t,帧缓冲首地址类型是color_t *

color_t* buffer = getbuffer((PIMAGE)NULL);

  得到帧缓冲首地址后,我们便可以直接读取和修改帧缓冲中的值。

2.1.2 获取帧缓冲大小

  帧缓冲区保存的颜色值数量和矩形区域中的像素数量一致,假设矩形区域宽高分别为 width\mathrm{width}width 和 height\mathrm{height}height 个像素,那么缓冲区所保存的颜色数量为 width×height\mathrm{width} \times \mathrm{height}width×height。
  窗口和图像的宽高可以通过 getwidth()getheight() 获取。

int width = getwidth();
int height = getheight();

2.1.3 通过读写帧缓冲数据修改像素颜色

  帧缓冲是块连续的内存区域,我们得到的仅仅是它的首地址,相当于是要访问一个一维数组,所以还需要知道坐标为 (x,y)(x,y)(x,y) 的像素在帧缓冲中的索引。
  图像中的像素颜值值会按行连续存储在帧缓冲中,而坐标 (x,y)(x,y)(x,y) 的像素位置在第 yyy 行第 xxx 列,所以对应的索引为 y×width+xy \times \mathrm{width} + xy×width+x。

//设置坐标为 (x, y)的像素颜色
buffer[y * width  + x] = color;

示例代码

//获取帧缓冲首地址
color_t* frameBuffer = getbuffer((PIMAGE)NULL);
//获取帧缓冲的大小
int width = getwidth(), height = getheight();//将坐标为(x, y)的像素颜色值设置成白色
int x = 100, y = 200;
frameBuffer[x + y * width] = WHITE;//获取坐标为(x, y)的像素颜色值
color_t pixelColor = frameBuffer[x + y * width]

  如上所示便能直接通过帧缓冲首地址来进行绘图或读取像素数据。
  如果想这样绘图,有一个事项需要特别注意,那就是EGE会自动检测有没有进行绘图操作,以免进行一些不必要的数据传输操作。如果没有调用EGE的绘图函数,是检测不到绘图的,所以很可能就不会显示出来。
  所以如果想要通过帧缓冲首地址来修改像素数据,需要加一些特别的操作来让图像显示,如调用 delay_fps() 等函数。

#include <graphics.h>
#include <math.h>double clamp(double value, double min, double max)
{return (value < min) ? min : ((value > max) ? max : value);
}int main()
{initgraph(640, 640, INIT_RENDERMANUAL);//获取帧缓冲首地址color_t* frameBuffer = getbuffer((PIMAGE)NULL);int width = getwidth((PIMAGE)NULL);int height = getheight((PIMAGE)NULL);float xCenter = width / 2.0f, yCenter = height / 2.0f;color_t bottomColor = EGERGB(0, 180, 255);color_t topColor = EGERGB(230, 230, 0);//通过帧缓冲来进行绘图for (int y = 0; y < height; y++) {for (int x = 0; x < width; x++) {//计算到中心距离的平方double distanceSquare = (x - xCenter) * (x - xCenter) + (y - yCenter) * (y - yCenter) ;double t = (cos(distanceSquare/ (60 * 60)) + 1.0) / 2.0;t = clamp(t, 0.0, 1.0);//计算当前像素颜色unsigned char r = round((1 - t) * EGEGET_R(bottomColor) + t * EGEGET_R(topColor));unsigned char g = round((1 - t) * EGEGET_G(bottomColor) + t * EGEGET_G(topColor));unsigned char b = round((1 - t) * EGEGET_B(bottomColor) + t * EGEGET_B(topColor));color_t color = EGERGB(r, g, b);//修改帧缓冲像素颜色数据frameBuffer[x + y * width] = color;}}//调用delay_fps()函数,让图像显示delay_fps(60);getch();closegraph();return 0;
}

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(1);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 = 2.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(1);closegraph();return 0;
}

EGE专栏:EGE专栏

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

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

EGE基础入门篇(八):清屏与重绘相关推荐

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

    EGE专栏:EGE专栏 上一篇:EGE基础入门篇(八):清屏与重绘 下一篇: 文章目录 一.双缓冲机制 1. 单缓冲绘图 1.1 单缓冲绘图的缺点 1.2 系统读取帧缓冲 2. 双缓冲绘图 2.1 双 ...

  2. EGE基础入门篇(七):组合图形

    EGE专栏:EGE专栏 上一篇:EGE基础入门篇(六):基本图形 下一篇:EGE基础入门篇(八):清屏与重绘 一. 组合图形 1. 复杂图形由基本图形组合而成   复杂的图形可以由基本图形组合而成,如 ...

  3. EGE基础入门篇(六):基本图形

    EGE专栏:EGE专栏 上一篇:EGE基础入门篇(五):窗口简单操作 下一篇:EGE基础入门篇(七):组合图形 一.EGE提供的基本图形 EGE绘制图形相关库函数文档 https://xege.org ...

  4. EGE基础入门篇(二):开始使用EGE

    EGE专栏:EGE专栏 上一篇:EGE基础入门篇(一):绘图基础知识 下一篇:EGE基础入门篇(三):开场动画 EGE基础入门篇(二) 文章最后修改时间:2021年6月23日19:30:47 文章目录 ...

  5. EGE基础入门篇(三):开场动画

    EGE专栏:EGE专栏 上一篇:EGE基础入门篇(二):开始使用EGE 下一篇:EGE基础入门篇(四):窗口简单操作 文章目录 开场动画 1. EGE开场动画的默认方式 2. 开场动画的开启 3. 开 ...

  6. netty 客户端断开 异常处理_netty案例,netty4.1基础入门篇八《NettyClient半包粘包处理》发数据方式》...

    小傅哥 | https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获.专注于原创专题案例编写,目前已完成的专题有:Netty4.x实战专题案例.用Java实现JVM.基于Ja ...

  7. Linux基础入门篇知识回顾

    Linux基础入门篇知识回顾 一.回顾书籍 二.基础知识 1.计算机基础知识 1.1计算机的特点及发展趋势 ①特点 ②发展趋势 1.2计算机系统组成 ①计算机硬件概念 ②计算机硬件各部分功能 ![在这 ...

  8. 《Ansible权威指南 》一 第一篇 Part 1 基础入门篇

    本节书摘来自华章出版社<Ansible权威指南 >一书中的第1章,第1.1节,李松涛 魏 巍 甘 捷 著更多章节内容可以访问云栖社区"华章计算机"公众号查看. 第一篇 ...

  9. linux入门_Linux超详细0基础入门篇(一)

    首先要感谢大康老师对我在Linux操作系统上的教导. 今天来讲一下用途广泛的Linux的基础入门教程 仅仅是做入门使用,如果想更加深入的学习那就需要自己做探索了. 本次例子使用的是kali linux ...

最新文章

  1. 插入脚注把脚注标注删掉_地狱司机不应该只是英国电影历史数据中的脚注,这说明了为什么...
  2. 计算机基础:程序、进程、线程
  3. 桌面制作——Wallpaper Engine+Rainmeter
  4. 苹果自研5nm芯片M1首次亮相,搭载新MacBook Air
  5. Oracle11新特性:分区功能增强-Oracle新增复合分区 (转载)
  6. linux下启动nfs服务,linux下Samba服务和NFS服务配置的方法
  7. C++动态空间申请、动态对象(new与delete运算)
  8. 44 万条数据揭秘:如何成为网易云音乐评论区的网红段子手?
  9. SQL Server-【知识与实战VIII】触发器(上)
  10. Linux内核通知链(Notifier)
  11. mysql安装包下载
  12. DS3231时钟模块使用,IIC协议实践。(基于STM32)
  13. php spider 参数详解,利用phpspider爬取网站数据
  14. java错误报告过滤_vue 过滤器filters的使用以及常见报错小坑(Failed to resolve filter)...
  15. 流媒体协议(三):FLV协议
  16. 笔记本超频会烧吗_笔记本cpu可以超频吗。有什么危害。怎么超频
  17. 低多边形,通过PS制作低多边形图形
  18. 一种通过物理分离实现WSUS伸缩性的方案
  19. SAN存储的局限性相关介绍
  20. cell数据如何删除重复项

热门文章

  1. 软银、诺基亚、宝马、国美、Lazada、搜狗等公司高管变动
  2. 基于ZTMapGIS打造智慧环保指挥中心平台,强化环境监测精细化管理
  3. innodb对B树游标的定位过程以及对“小于(等于)B树最小记录”的特殊处理
  4. 【高等数学基础进阶】多元函数的极值与最值
  5. pytorch实现kaggle猫狗识别(超详细)
  6. 百科园c语言实验报告,C语言上机题库百科园第6章!南信大!
  7. HNU计算机系统实验shlab3shlab4
  8. gcc找不到Linux/in,已安装GCC,但找不到命令
  9. git查看远程仓库地址命令:
  10. JPG免费转PDF的方法分享