我一直在考虑跨平台的GUI,一个问题引出了下一个问题,最后我问道:像素是如何出现在显示器上的?一个相当整齐的堆栈溢出响应回答:
监视器上显示的图像以一种称为帧缓冲区的结构存储在显卡上的计算机视频RAM中。
视频RAM中的数据可以由GPU或CPU生成。显卡上的专用DMA组件连续读取视频RAM并将其发送到监视器。输出到显示器的信号可以是模拟信号(VGA),其中颜色分量在离开卡之前通过数模转换器发送,或者在HDMI或DVI的情况下是数字信号。
现在,您可能意识到,对于一个每像素4字节的1920x1080显示器,您只需要大约8MB来存储图像,但是您计算机中的视频RAM可能是这个大小的许多倍。这是因为视频RAM不仅仅用于存储帧缓冲区。视频RAM直接连接到GPU,GPU是专为高效的3D渲染和视频解码而设计的专用处理器。
这是非常丰富的信息,也解释了为什么我的显示器通过DVI直接连接到我的GPU。另一个答案是,我发现没有现代操作系统会让你直接访问帧缓冲区。但幸运的是,这是错误的。有了另一个StackexchangeTip,它就像以下内容一样简单:
>;cat/dev/urandom>;/dev/fb0-bash:/dev/fb0:>;sudo cat/dev/urandom>;/dev/fb0-bash:/dev/fb0-bash:/dev/fb0:拒绝权限>;ls-al/dev/fb0crw-rw-1根视频29,0 4月4 00:21/dev/fb0&gw。
为了掩饰sudo为什么会神秘地不起作用,下面向显示器写出一串噪音:
太棒了!内核帧缓冲区文档有一些一般信息,但要点是/dev/fb0的行为类似于/dev中的任何其他内存设备,我们可以像文件一样写入它。因为它只是一个内存缓冲区,所以任何写入都将覆盖现有的值,当控制台提示立即覆盖噪声时,您可以看到该值。
此外,您甚至可以读取帧缓冲区,使用cp/dev/fb0截屏进行截图,这在我用手机拍一大堆照片之前会很有帮助。
好的,StackExchange的答案也给出了一个写出红色方框的脚本,但是我想尝试一下渐变。在排列了以下几行数次之后,我决定:监视器一次写到一个水平线上,蓝色、绿色、红色、阿尔法(?)有一个字节。每像素(看起来像24位真彩色)。
xsize=1920 ysize=1200(';data.out';,';wb';)as f:for y in range(0,ysize):for x in range(0,xsize):r=int(min(x/(xsize/256),255)g=int(min(y/(ysize/256),255))b=0 f.。写((B).。TO_BYTES(1,byteorder=';Little';)f.。写((G).)。TO_BYTES(1,byteorder=';Little';)f.。写((R).。TO_BYTES(1,byteorder=';Little';)f.。Write((0)。TO_BYTES(1,字节顺序=';小';)。
我一直在Linux控制台(而不是x服务器或其他显示服务器)上运行它,我认为它只是将文本和基本颜色呈现到帧缓冲区。我应该也能做到这一点。我抓起柠檬,这是我以前用过的一种位图字体。位图字体将字符描述为位网格,无需过多考虑文本渲染、别名等方面的复杂性/恐怖,即可轻松渲染。
我开始了一个简陋的BDF文件阅读器,然后开始后悔这个想法和我的python脚本,然而,我从字体中生成了一个单独的“锁定”图标,画到了帧缓冲区!这可以扩展到写出一个文本字符串,但是同样,它真的很不可靠。
好的,所以在这一点上,将图像渲染到帧缓冲区并不是一件很麻烦的事情。我们已经获得了平滑的颜色渐变,图像只是一种文件格式,我们必须读取像素数据并将其编码/转储到帧缓冲区。我将图像导出为TGA文件(为了便于解析,这只是一种未压缩的格式,也尝试了BMP),并使用pyTGA将图像直接写出来。巧妙的是,您可以使用Seek跳过像素,而不是让OffsetWrite输出黑色像素。
导入sys导入操作系统导入TGA,条件是len(sys。argv)<;4:打印(";用法:tga.py xsize xoff yoff输入输出";)sys。exit(1)g_xsize=int(sys.。argv[1])g_xoff=int(sys.。argv[2])g_yoff=int(sys.。argv[3])g_input=sys。argv[4]g_output=sys。argv[5]img=TGA。image()img。加载(G_Input)标题=img。以f:f形式打开(g_output,';wb';)的标题(_HEADER)。查找(g_yoff*4*g_xsize,os。SEEK_CUR)表示范围内的y(标题。IMAGE_HEIGHT):f。查找(g_xoff*4,操作系统。SEEK_CUR)范围内的x(标题。image_width):P=img。get_Pixel(y,x)f。Write(p[2]。TO_BYTES(1,byteorder=';Little';)f.。Write(p[1].。TO_BYTES(1,byteorder=';Little';)f.。Write(p[0]。TO_BYTES(1,byteorder=';Little';)f.。Write((0)。TO_BYTES(1,byteorder=';Little';)f.。Seek((g_xsize-标题。image_width-g_xoff)*4,操作系统。查找(_CUR)。
这对连接我脑海中的一堆松散的线非常有用。例如,它解释了为什么OpenGL的最后一步是渲染到帧缓冲区,以及为什么双缓冲(渲染到隐藏缓冲区,然后交换)会有帮助(特别是考虑到我的python程序有多慢)。
在GUI方面,根据我所发现的,很多自定义GUI引擎大多只是将位图和X11组合在一起,还有许多其他引擎(“MPlayer、Links2、Netsurf、fbia、[2]和fim[3]以及GGI、SDL、GTK+和Qt”等库)渲染到帧缓冲区。一些大学班级甚至使用Raspberry PI来摆弄帧缓冲区,那里有一些有用的读物,因为它的架构非常简单。