frame buffer :帧缓存、帧缓冲
帧缓冲区就像是屏幕的“草稿纸”,图形处理器(GPU)在上面绘制图像,然后将结果呈现到屏幕上。
在嵌入式系统中,例如Linux系统下,可以通过访问特定的设备节点(如/dev/fb0)来直接读写帧缓冲区,从而实现图形输出或进行底层的图形编程
允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作
对于应用层,通过操作/dev/fb*,通过ioctl来用各种命令控制fb
/usr/include/linux/fb.h 中查看
在Linux系统中,通过/dev/fb0设备文件可以访问帧缓冲区,从而直接控制显示器的显示内容。
帧缓冲区的工作原理
- 图像渲染:GPU在帧缓冲区中渲染图像,这意味着它计算每个像素的颜色值并存储在帧缓冲区的相应位置。
- 刷新屏幕:一旦一帧图像在帧缓冲区中完全渲染完毕,它就会被“刷新”到显示器上,这就是我们看到的画面。
- 双缓冲或多缓冲:为了平滑动画效果并避免画面撕裂,现代系统通常使用双缓冲或多缓冲技术。在这种情况下,有两个或更多的帧缓冲区,一个用于显示,另一个用于渲染下一帧。当新的一帧渲染完成后,两个缓冲区的角色会互换,这样就可以无缝地显示连续的帧,而不会出现视觉上的中断。
颜色编码方式
RGB888是一种24位颜色模式,其中“888”分别代表红色、绿色和蓝色各占用8位(bits)。
这意味着每种颜色都有2^8=256个级别,从0到255。因此,总的颜色数量是256 * 256 * 256 = 16,777,216种。由于每个像素使用三个字节,所以这种模式下的图像文件相对较大,但能提供非常丰富的色彩表现。
RGB565RGB565是一种16位颜色模式,其中红色和蓝色各占用5位,绿色占用6位。“565”表示红、绿、蓝的位数分配。具体而言,红色和蓝色各有2^5=32个级别,绿色有2^6=64个级别。因此,总的颜色数量是32 * 64 * 32 = 65,536种。
与RGB888相比,RGB565颜色数量较少,但是每个像素只使用两个字节,因此更节省空间,常用于对存储空间敏感的应用场合,如移动设备和嵌入式系统。
RGB888 -------->PC /4412
RGB 565 --------->S3C2440
RGB565 兼容RGB888
用frame buffer显示操作步骤
- 打开显示设备(/dev/fb0)
- 获取显示设备相关参数(分辨率、像素深度)
- 建立内存映射
- 写入颜色值
- 解除映射
- 关闭设备
Framebuffer 是什么 ?简单说下原理? 怎么做的实现了什么?
通过内存映射的,把用户空间映射到显存空间
Framebuffer(帧缓冲)是计算机系统中用于图形显示的一种机制,主要用于将屏幕显示的内容映射到内存中的一个特定区域。这个内存区域被称为帧缓冲区,其中包含了屏幕上的每一个像素点的颜色和属性信息。
原理:
1. 抽象化:Framebuffer将显示硬件的复杂性进行了抽象,提供了一个统一的接口给上层软件使用,使得软件无需关心具体的显示硬件细节,就可以直接操作屏幕显示。
2. 内存映射:在Framebuffer机制下,显示缓冲区(即帧缓冲区)通常是一块连续的物理内存区域,这块内存中的每一部分对应着屏幕上的一个像素点。通过将这块物理内存映射到进程的虚拟地址空间,上层软件可以直接读写这块内存,从而控制屏幕显示。
3. 直接访问:应用程序可以直接对帧缓冲区进行读写操作,写入缓冲区的操作会立即反映在屏幕上,而读取操作则可以获取屏幕状态。
4. 硬件无关性:Framebuffer驱动通常为不同的显示硬件提供一个统一的接口,这意味着不同的显示硬件可以使用同一个Framebuffer API,简化了软件开发。
实现:
在Linux系统中,Framebuffer是作为一个字符设备实现的,其设备文件通常位于 /dev/fb*(例如 /dev/fb0),主设备号为29。Framebuffer设备驱动负责管理帧缓冲区的内存,以及与硬件的交互。应用程序通过系统调用(如read、write、ioctl等)与Framebuffer设备通信,进行读写操作。
达到的效果:
1. 直接写屏:应用程序可以直接写入帧缓冲区,实现屏幕更新,而不必通过复杂的图形API或硬件特定的指令集。
2. 统一接口:为不同硬件提供了统一的访问接口,简化了上层软件的设计和移植。
3. 硬件抽象:屏蔽了硬件差异,使得上层软件可以不考虑硬件细节,提高了软件的可移植性和可维护性。
4. 性能提升:由于直接操作内存,相比于经过多层软件栈的图形渲染,可以实现较高的显示性能。Framebuffer在嵌入式系统、服务器系统和一些对图形性能要求不是非常高的场景中使用较为广泛。
用RGB排线接的,240*320
#ifndef _FRAMEBUFFER_H_
#define _FRAMEBUFFER_H_
#include "utf.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);
extern void draw_x_line(int x, int y, int len, unsigned int col);
extern void draw_y_line(int x, int y, int len, unsigned int col);
extern void draw_rectangle(int x, int y, int w, int h, unsigned int col);
extern void draw_circle(int x, int y, int r, unsigned int col);
extern void draw_bmp(char *bmpname, int w, int h);
extern void draw_gb2312(int x,int y,unsigned char*font_yan,int high,int len, unsigned int col); /*打印字幕函数*/
extern int draw_one_ascii(int x, int y, char ch, unsigned int fcolor, unsigned int bcolor);
extern int draw_string(int x, int y, char *pstr, unsigned int fcolor, unsigned int bcolor);
extern int draw_utf8(UTF8_INFO *info, int x, int y, char* zi, unsigned int col, unsigned int col1);
extern int draw_utf8_str(UTF8_INFO *info, int arg_x, int arg_y, char* zi, unsigned int col, unsigned int col1);
#endif
#include <stdio.h>
#include <linux/fb.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <math.h>
#include <stdlib.h>
#include "font.h"
#include "framebuffer.h"
struct fb fbinf_g;
/*
*初始化显示设备
* */
int init_fb(const char *fbname)
{
//1、打开显示设备
int fd = open(fbname, O_RDWR);
if (-1 == fd)
{
perror("fail open fb");
return -1;
}
struct fb_var_screeninfo vinf;
//2、获取显示设备相关参数:分辨率、像素深度
int ret = ioctl(fd, FBIOGET_VSCREENINFO, &vinf);
if (ret < 0)
{
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);
fbinf_g.bits = vinf.bits_per_pixel;
fbinf_g.x_virtual = vinf.xres_virtual;
fbinf_g.y_virtual = vinf.yres_virtual;
fbinf_g.fd = fd;
//3、建立内存映射关系:将显存空间映射到用户空间
size_t size = vinf.xres_virtual*vinf.yres_virtual*vinf.bits_per_pixel/8;
fbinf_g.pmem = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if ((void *)-1 == fbinf_g.pmem)
{
perror("fail mmap");
return -1;
}
return 0;
}
/*
*绘制一个像素点
* x,y 像素点横轴和纵轴坐标
* col 像素点的颜色
* */
void draw_point(int x, int y, unsigned int col)
{
if (x >= fbinf_g.x_virtual || y >= fbinf_g.y_virtual)
{
return ;
}
if (fbinf_g.bits == RGB888_FMT)
{
unsigned int *p = fbinf_g.pmem; //RGB888
*(p+y*fbinf_g.x_virtual + x) = col;
}
else if (fbinf_g.bits == RGB565_FMT)
{
unsigned short *p = fbinf_g.pmem; //RGB565
*(p+y*fbinf_g.x_virtual + x) = col;
}
}
/*
*反初始化显示设备
* 解除映射关系,释放内存空间
* 关闭显示设备
* */
void uninit_fb()
{
munmap(fbinf_g.pmem, fbinf_g.x_virtual*fbinf_g.y_virtual*fbinf_g.bits/8);
close(fbinf_g.fd);
}
void draw_x_line(int x, int y, int len, unsigned int col)
{
for (int i = x; i < x+len; i++)
{
draw_point(i, y, col);
}
}
void draw_y_line(int x, int y, int len, unsigned int col)
{
for (int i = y; i < y+len; i++)
{
draw_point(x, i, col);
}
}
void draw_rectangle(int x, int y, int w, int h, unsigned int col)
{
draw_x_line(x, y, w, col);
draw_y_line(x, y, h, col);
draw_x_line(x, y+h, w, col);
draw_y_line(x+w, y, h, col);
}
void draw_circle(int x, int y, int r, unsigned int col)
{
int x0, y0;
for (float si = 0; si <= 360; si += 0.1)
{
x0 = r * cos(2 * 3.1415/360 * si) + x;
y0 = r * sin(2 * 3.1415/360 * si) + 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_bmp(char *bmpname, int w, int h)
{
int fd = open(bmpname, O_RDONLY);
if (-1 == fd)
{
perror("fail open bmp");
return ;
}
lseek(fd, 54, SEEK_SET);
unsigned char *pic = malloc(w * h * 24 / 8);
read(fd, pic, w*h*24/8);
unsigned char *p = pic;
for (int i = h-1; i >= 0; i--)
{
for (int j = 0; j < w; j++)
{
unsigned char r, g, b;
b = *p; p++;
g = *p; p++;
r = *p; p++;
if (fbinf_g.bits == RGB888_FMT)
{
unsigned int col = r << 16 | g << 8 | b << 0;
draw_point(j, i, col);
}
else if (fbinf_g.bits == RGB565_FMT)
{
unsigned short col = (r >> 3) << 11 | (g >> 2) << 5 | (b >> 3);
draw_point(j, i, col);
}
}
}
free(pic);
close(fd);
}
/*使用字模绘制一个文字
* x,y 字模显示的起始位置
* font_yan 字模首地址
* high 字模的高度
* len 字模的宽度(以字节为单位)
* */
void draw_gb2312(int x,int y,unsigned char*font_yan,int high,int len, unsigned int col) /*打印字幕函数*/
{
int i, j, k;
unsigned char temp;
for(i=0;i<high;i++)
{
for(k=0;k<len;k++)
{
temp=font_yan[i*len+k];
for(j=0;j<8;j++)
{
if(temp & 0x80)
{
draw_point(8*k+x+j, y+i, col);
}
else
{
//draw_point(8*k+x+j, y+i, 0x00ffffff);
}
temp=temp<<1;
}
}
}
}
int draw_one_ascii(int x, int y, char ch, unsigned int fcolor, unsigned int bcolor)
{
int i = 0;
int j = 0;
char tmp = 0x80;
for (j = 0; j < 16; j++)
{
tmp = 0x80;
for (i = 0; i < 8; i++)
{
if (fontdata_8x16[ch*16 + j] & tmp)
{
draw_point(x+i, y+j, fcolor);
}
else
{
//draw_point(x+i, y+j, bcolor);
}
tmp >>= 1;
}
}
return 0;
}
int draw_string(int x, int y, char *pstr, unsigned int fcolor, unsigned int bcolor)
{
char *ptmp = pstr;
int i = 0;
while (*ptmp != '\0')
{
draw_one_ascii(x+i*10, y, *ptmp, fcolor, bcolor);
i++;
ptmp++;
}
return 0;
}
int draw_utf8(UTF8_INFO *info, int x, int y, char* zi, unsigned int col, unsigned int col1)
{
unsigned long out = 0 ;
int ret = enc_utf8_to_unicode_one((unsigned char*)zi,&out);
unsigned char* data = get_utf_data(info,out);
unsigned char temp = 0 ;
unsigned int i,j,k;
unsigned int num = 0;
for(i=0;i<info->height;i++)
{
for(j=0;j<info->width/8;j++)
{
temp = data[num++];
for(k=0;k<8;k++)
{
if(0x80&temp)
{
draw_point(x+k+j*8, y+i, col);
}
else
{
//draw_point(x+k+j*8, y+i, col1);
}
temp= temp<<1;
}
}
}
return ret;
}
int draw_utf8_str(UTF8_INFO *info, int arg_x, int arg_y, char* zi, unsigned int col, unsigned int col1)
{
char* temp = zi;
unsigned int x = arg_x ;
unsigned int y = arg_y;
while(*temp != '\0')
{
int ret = draw_utf8(info, x, y, temp, col, col1);
x += info->width;
if(x > fbinf_g.x_virtual)
{
x = 0;
y += info->height;
if(y > fbinf_g.y_virtual)
{
y = 0;
}
}
temp += ret;
}
return 0;
}
#include <stdio.h>
#include "utf.h"
#include "framebuffer.h"
unsigned char pu[24*24/8] = {
/*-- 文字: 普 --*/
/*-- 仿宋18; 此字体下对应的点阵为:宽x高=24x24 --*/
0x00,0x00,0x00,0x00,0x03,0x00,0x01,0xC3,0x80,0x00,0xE3,0x00,0x00,0x77,0x00,0x00,
0x6F,0xF0,0x0F,0xFE,0x10,0x00,0x7E,0xE0,0x06,0x7E,0xE0,0x03,0x7F,0xC0,0x03,0xFF,
0x80,0x01,0x7F,0xFC,0x7F,0xFF,0x86,0x30,0x00,0x00,0x03,0x87,0xC0,0x03,0xFF,0xC0,
0x03,0x81,0xC0,0x03,0x8F,0xC0,0x01,0xFF,0x80,0x01,0x81,0x80,0x01,0x87,0x80,0x03,
0xFF,0x80,0x01,0x81,0x80,0x00,0x00,0x00
};
UTF8_INFO utf8_info;
int main(int argc, const char *argv[])
{
init_fb("/dev/fb0");
bzero(&utf8_info, sizeof(UTF8_INFO));
strcpy(utf8_info.path, ZIKUK_FILE_BIG);
utf8_info.width = 32;
utf8_info.height = 32;
init_utf8(&utf8_info);
draw_point(400, 300, 0x00ff0000);
draw_point(200, 300, 0x00ffff00);
draw_x_line(100, 100, 200, 0x0000ff00);
draw_rectangle(50, 50, 100, 200, 0x00ff0000);
draw_circle(300, 300, 100, 0x0000ffff);
sleep(2);
draw_bmp("./1.bmp", 800, 600);
draw_gb2312(400, 400, pu, 24, 24/8, 0x0000ffff);
draw_string(200,100, "hello", 0x00ffff00, 0x00000000);
double tmp = 23.5;
char buff[32] = {0};
sprintf(buff, "%f", tmp);
draw_string(300, 100, buff, 0x00ff0000, 0x00000000);
draw_utf8_str(&utf8_info, 100, 300, "大家好!", 0x00ff0000, 0x00000000);
uninit_utf8(&utf8_info);
uninit_fb();
return 0;
}