Framebuffer(帧缓冲)是Linux系统中为显示设备提供的一套应用程序接口,它将显存抽象为一种设备,允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。
原理:通过内存映射技术向显存空间中写入rgb颜色值;
1. 基本概念
- 定义:Framebuffer,可以译作“帧缓冲”,是Linux内核为显示设备提供的一个接口,它将显存抽象后供用户态进程使用。
- 作用:作为显示内存的一个映像,Framebuffer允许应用程序直接对其进行读写操作,从而控制屏幕上的显示内容。
2. 组成部分
- 颜色缓冲区:存储每个像素的颜色信息,如RGBA值。
- 深度缓冲区:存储每个像素的深度信息,用于处理3D场景的遮挡问题。
- 模板缓冲区:用于实现图形遮罩技术,控制哪些像素可以被绘制。
- 多重采样缓冲区(MSAA):用于减少锯齿状边缘的效果,提高图像质量。
3. 使用方式
1.打开显示设备(/dev/fbo);
2.获取显示设备相关参数(分辨率,位深度);
3.建立内存映射;
4.写入rgb颜色值;
5.解除映射;
6.关闭显示设备;
4. 应用场景
- 图形界面显示:在Linux桌面系统中,X Window服务器利用Framebuffer进行窗口的绘制。
- 嵌入式系统:在嵌入式Linux系统中,Framebuffer是控制LCD显示的主要方式。
- 游戏开发:在游戏开发中,Framebuffer用于渲染游戏画面,并通过更新Framebuffer的内容来实现动画效果。
5. 优点与缺点
- 优点:
- 提供了对图形设备的硬件抽象,简化了图形编程的复杂性。
- 支持离屏渲染,提高了图形处理的性能。
- 允许应用程序直接控制屏幕显示内容,灵活性高。
- 缺点:
- 需要真正的显卡驱动支持,且所有显示任务都由CPU完成,可能导致CPU负担较重。
- 对物理显存的位置、换页机制等具体细节进行了抽象,但开发者仍需了解相关硬件知识以进行高效编程。
6. 编程接口
- open函数:用于打开Framebuffer设备文件(如/dev/fb0)。
- ioctl函数:用于获取和设置Framebuffer的参数,如分辨率、颜色深度等。
- mmap函数:用于将Framebuffer映射到进程的地址空间,以便进行读写操作。
7.示例
#include "framebuffer.h"
#include <linux/fb.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <math.h>
void *pmem;
struct fb_var_screeninfo vinf;
int init_fb(char *devname)
{
//1. 打开显示设备
int fd = open(devname, O_RDWR);
if (-1 == fd)
{
perror("fail open fb");
return -1;
}
//2、获取显示设备相关参数 分辨率 位深度
int ret = ioctl(fd, FBIOGET_VSCREENINFO, &vinf);
if (-1 ==ret)
{
perror("fail ioctl");
return -1;
}
printf("xres = %d, yres = %d\n", vinf.xres, vinf.yres);
printf("xres_virtual = %d, yres_virtual = %d\n", vinf.xres_virtual, vinf.yres_virtual);
printf("bits_per_pixel : %d\n", vinf.bits_per_pixel);
size_t len = vinf.xres_virtual * vinf.yres_virtual * vinf.bits_per_pixel/8;
//3, 建立显存和用户空间的映射关系
pmem = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if ((void *)-1 == pmem)
{
perror("fail mmap");
return -1;
}
return fd;
}
void draw_point(int x, int y, unsigned int col)
{
if (x >= vinf.xres || y >= vinf.yres)
{
return ;
}
if (vinf.bits_per_pixel == RGB888_FMT)
{
unsigned int *p = (unsigned int *)pmem;
*(p + y * vinf.xres_virtual + x) = col;
}
else if (vinf.bits_per_pixel == RGB565_FMT)
{
unsigned short *p = (unsigned short *)pmem;
*(p + y * vinf.xres_virtual + x) = col;
}
return ;
}
void draw_x_line(int x, int y, int len, unsigned int col)
{
if (x >= vinf.xres || y >= vinf.yres)
{
return ;
}
for (int i = x; i < x+len; i++)
{
draw_point(i, y, col);
}
return;
}
void draw_y_line(int x, int y, int len, unsigned int col)
{
if (x >= vinf.xres || y >= vinf.yres)
{
return ;
}
for (int i = y; i < y+len; i++)
{
draw_point(x, i, col);
}
return;
}
void draw_bias(int x, int y, int len, unsigned int col)
{
if (x >= vinf.xres || y >= vinf.yres)
{
return ;
}
int i=0;
while(i<len)
{
draw_point(x, y, col);
++x;
++y;
++i;
}
}
void draw_circle(int x, int y, int r, unsigned int col)
{
int x0, y0;
for (int i = 0; i <= 360; ++i)
{
x0 = r * cos(2 * 3.1415/360 * i) + x;
y0 = r * sin(2 * 3.1415/360 * i) + y;
draw_point(x0, y0, col);
draw_point(x0+1, y0, col);
draw_point(x0, y0+1, col);
draw_point(x0-1, y0, col);
draw_point(x0, y0-1, col);
}
}
void draw_rectangle(int x,int y,int len,int wide,unsigned int col)
{
if (x >= vinf.xres || y >= vinf.yres)
{
return ;
}
draw_x_line(x,y,len,col);
draw_y_line(x,y,wide,col);
draw_x_line(x,y+wide,len,col);
draw_y_line(x+len,y,wide,col);
}
void draw_clear(unsigned int col)
{
for(int i =0;i<vinf.xres_virtual;++i)
{
for(int j=0;j<vinf.yres_virtual;++j)
{
draw_point(i,j,col);
}
}
}
void uninit_fb(int fd)
{
size_t len = vinf.xres_virtual * vinf.yres_virtual * vinf.bits_per_pixel/8;
munmap(pmem, len);
close(fd);
}