FrameBuffer,可以译作帧缓存,有时候简称为fbdrv。
这是一种独立于硬件的抽象图形设备。
是Linux为显示设备提供的一个接口,把显存抽象后的一种设备,允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。
如何控制framebuffer
对于应用层,通过操作/dev/fb*,通过ioctl来用各种命令控制framebuffer
我们可以在**/usr/include/linux/fb.h** 中查看
进入内核源码中看看<linux/fb.h>,里面定义了一些ioctl的命令
#define FBIOGET_VSCREENINFO 0x4600 //获取应用程序可改变的参数(如设定的分辨率)
#define FBIOPUT_VSCREENINFO 0x4601
#define FBIOGET_FSCREENINFO 0x4602 //获取固定的参数(如屏幕的分辨率,一般只是拿来看看)
#define FBIOGETCMAP 0x4604
#define FBIOPUTCMAP 0x4605
#define FBIOPAN_DISPLAY 0x4606
<linux/fb.h>中还提供了专门的结构体类型,用来存放上述两个参数,如下就是存放可变参数的结构体类型
__u32 xres; /* visible resolution 可视画面的x、y轴分辨率 */
__u32 yres;
__u32 xres_virtual; /* virtual resolution 虚拟画面的x、y轴分辨率 */
__u32 yres_virtual;
__u32 xoffset; /* offset from virtual to visible 可视画面相对于虚拟画面的x、y轴偏移量*/
__u32 yoffset; /* resolution */
__u32 bits_per_pixel; /* guess what 像素深度 */
__u32 grayscale; /* != 0 Graylevels instead of colors */
struct fb_bitfield red; /* bitfield in fb mem if true color, */
struct fb_bitfield green; /* else only length is significant */
struct fb_bitfield blue;
struct fb_bitfield transp; /* transparency */
__u32 nonstd; /* != 0 Non standard pixel format */
__u32 activate; /* see FB_ACTIVATE_* */
__u32 height; /* height of picture in mm */
__u32 width; /* width of picture in mm */
__u32 accel_flags; /* (OBSOLETE) see fb_info.flags */
#include <sys/mman.h>
在进行帧缓存时,我们首先要进行内存映射,所有就需要用到此头文件#include <sys/mman.h>
首先是mmap函数,其功能是开启内存映射,munmap函数,是解除虚拟内存映射
mmap函数
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
功能:内存映射
参数:
addr:欲映射内存的起始地址,NULL表示系统自动选定,映射
成功后返回该地址
length:映射内存大小
prot:映射区域的保护方式(读/写/执行/不能存取)
flags:影响区域映射的各种特性
MAP_SHARED:对映射区域写入的数据会复制回源文件内,
而且允许其他映射该文件的进程共享
MAP_PRIVATE:对映射区域的写入操作会产生一个映射文件的复制,
即对此区域的任何修改都不会写回原来的文件内
fd:要映射的目标文件
offset:文件映射的偏移量(0代表从开头开始)
返回值:
void * :返回映射欲映射内存的首地址
eg:
unsigned int* addr = mmap(NULL, 800*480*4, PROT_READ|PROT_WRITE,MAP_SHARED, fd, 0);
munmap函数
int munmap(void *addr, size_t length);
功能:解除内存映射
参数:
addr:映射内存的地址
length:映射内存大小
#include <sys/ioctl.h>
int ioctl(int fd, int request, ...);
ioctl(fd, FBIOGET_VSCREENINFO, &vinfo); //获取屏幕信息(可变)
实战
接下来,我用几个例子来演示一下。
头文件
#ifndef _FRAMEBUFFER_H_
#define _FRAMEBUFFER_H_
#define RGB888_FMT 32 //GRB888图像格式
#define RGB565_FMT 16 //GRB565图像格式
/*framebuffer相关参数*/
struct fb
{
void *pmem; //映射的用户空间首地址
unsigned int bits; //像素深度(一个像素点颜色值的位数)
unsigned int x_virtual; //虚拟显存空间横向分辨率
unsigned int y_virtual; //虚拟显存空间纵向分辨率
int fd; //显示设备文件描述符
};
extern int init_fb(const char *fbname);
extern void uninit_fb();
extern void draw_point(int x, int y, unsigned int col);
#endif
初始化显示设备
在开始阶段,我们首先需要进行的操作是,打开**/dev/fb0**(初始化设备)
struct fb fbinfo_g;
//初始化显示设备
int InitFb(const char *fbname)
{
//打开显示设备,用读写方式
int fd = open(fbname,O_RDWR);
if(-1 == fd) //若打开失败,返回-1
{
perror("fail to fb"); //终端显示
return -1;
}
struct fb_var_screeninfo vinf;
//获取显示设备相关参数:分辨率、像素深度
int ret = ioctl(fd,FBIOGET_VSCREENINFO,&vinf);
if(0 > ret)
{
perror("fail to ioctl");
return -1;
}
printf("xres = %d,yres = %d\n",vinf.xres,vinf.yres); //输出可视画面的x、y轴分辨率
printf("xres_virtual = %d, yres_virtual = %d\n", vinf.xres_virtual, vinf.yres_virtual); //输出虚拟显存空间x、y轴分辨率
printf("bits_per_pixel = %d\n", vinf.bits_per_pixel); //输出像素深度
//将该值赋值到我们所写framebuffer相关参数的结构体当中
fbinfo_g.bits = vinf.bits_per_pixel;
fbinfo_g.x_virtual = vinf.xres_virtual;
fbinfo_g.y_virtual = vinf.yres_virtual;
fbinfo_g.fd = fd;
//建立内存映射关系:将显存空间映射到用户空间
size_t size = vinf.xres_virtual * vinf.yres_virtual * vinf.bits_per_pixel / 8; //除以8是因为像素深度单位是位,要将其转化为比特
fbinfo_g.pmem = mmap(NULL,size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if((void *)-1 == fbinfo_g.pmem)
{
perror("fail to mmap");
return -1;
}
return 0;
}
关闭显示设备
//关闭显示设备
void UnInitFb()
{
munmap(fbinfo_g.pmem,fbinfo_g.x_virtual * fbinfo_g.y_virtual * fbinfo_g.bits / 8);
close(fbinfo_g.fd);
}
如何画点
//绘制像素点
void DrawPoint(int x,int y,unsigned int col) //x坐标、y坐标、颜色
{
if(x >= fbinfo_g.x_virtual || y >= fbinfo_g.y_virtual)
{
return ; //判断如果已经大于其虚拟显存空间了,直接ruturn
}
//判断像素格式是888格式还是565格式,888代表r、g、b分别占8位,565类似。
if(fbinfo_g.bits == RGB888_FMT)
{
unsigned int *p = fbinfo_g.pmem; //p:映射的用户空间首地址
*(p + y*fbinfo_g.x_virtual + x) = col;
}
else if(fbinfo_g.bits == RGB565_FMT)//RGB565
{
unsigned short *p = fbinfo_g.pmem;
*(p+y*fbinfo_g.x_virtual + x) = col;
}
}
结果:
DrawPoint(500,400,0x00ff0000);
DrawPoint(500,500,0x00ff0000);
我们可以看到图右下角有两个小红点(有点小)。
如何画线
我们已经学会画点了,那么线就是画一连串的点即可。
//绘制横线
void DrawLine(int x,int y,int len,unsigned int col) //len表示线的长度
{
for(int i = x;i < x+len;++i)
{
DrawPoint(i,y,col);
}
}
//绘制纵线
void DrawLongLine(int x,int y,int len,unsigned int col)
{
for(int i = y;i < y+len;++i)
{
DrawPoint(x,i,col);
}
}
结果:
DrawLine(200,200,200,0x00ff0000);
DrawLongLine(300,300,200,0x00ff0000);
## 怎么画矩形
//绘制矩形
//参数3:宽度 参数4:高度
void DrawRectangle(int x,int y,int w,int h,unsigned int col)
{
DrawLine(x,y,w,col);
DrawLongLine(x,y,h,col);
DrawLongLine(x+w,y,h,col);
DrawLine(x,y+h,w,col);
}
结果:
DrawRectangle(100,100,200,300,0x00ff0000);
如何画圆
//绘制圆
void DrawCircle(int x,int y,int r,int col) //x、y可以获得中心点,r是半径
{
for(float si = 0;si <= 360;si += 0.1)
{
int x0 = r * cos(2 * 3.1415 / 360 * si) + x;
int y0 = r * sin(2 * 3.1415 / 360 * si) + y;
DrawPoint(x0,y0,col);
}
}
由于该函数运用到了sin、cos,我们需要添加头函数<math.h>,所以在编译时,后缀需要加上
-lm
结果:
DrawCircle(300,400,50,0x00ff0000);
以上就是今天内容,谢谢大家