linux编程----文件(framebuffer)
1、Framebuffer 基本概念
本质:
Linux 内核提供的显示设备抽象层
将显存映射为线性内存空间(设备文件:/dev/fbX)
应用程序通过读写内存直接控制屏幕像素
2.核心结构:
struct fb_var_screeninfo { // 可变参数
__u32 xres; // 可视区域宽(像素)
__u32 yres; // 可视区域高
__u32 xres_virtual; // 虚拟缓冲区宽
__u32 yres_virtual; // 虚拟缓冲区高
__u32 bits_per_pixel;// 像素深度(16/24/32)
// ... 其他参数
};
3.工作流程
打开显示设备(/dev/fb0)
获取显示设备相关参数(分辨率,像素格式)---》ioctl
建立显存空间和用户空间的内存映射
向映射的用户空间写入RGB颜色值
解除映射关系
关闭显示设备
2、关键代码实现解析
1. 初始化流程 (
init_fb
关键点:
xres_virtual
可能大于xres
(实现多缓冲)像素深度决定颜色格式:
16位:RGB565(R:5位, G:6位, B:5位)
32位:ARGB8888(含Alpha通道)
相关代码:
int init_fb(char *devname) {// 1. 打开设备fb = open(devname, O_RDWR); // 示例:open("/dev/fb0", ...)// 2. 获取屏幕参数ioctl(fb, FBIOGET_VSCREENINFO, &vinfo);// 3. 内存映射size_t len = vinfo.xres_virtual * vinfo.yres_virtual * vinfo.bits_per_pixel/8;pmem = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fb, 0);
}
2. 像素绘制算法 (
draw_point
)
地址计算:
offset = (y * 虚拟宽度 + x) * 像素字节数
内存布局:
像素(0,0) | 像素(1,0) | ... | 像素(W-1,0) 像素(0,1) | 像素(1,1) | ... | 像素(W-1,1) ...
相关代码
// 32位色模式
*(unsigned int*)(pmem + (y * vinfo.xres_virtual + x) * 4) = color;// 16位色模式
*(unsigned short*)(pmem + (y * vinfo.xres_virtual + x) * 2) = color;
3. 图形绘制算法
(1) 圆形绘制 (
draw_Circle
)
原理:参数方程离散化(x=r·cosθ, y=r·sinθ)
优化点:可改用Bresenham算法避免浮点运算
相关代码:
for(int angle = 0; angle <= 360; angle++) {int x0 = x + r * cos(angle * PI/180);int y0 = y + r * sin(angle * PI/180);draw_point(x0, y0, color);
}
(2) BMP图片绘制 (
draw_bmp
)
BMP特性:
文件头54字节
像素存储顺序:从下到上扫描线
颜色排列:BGR(非RGB)
相关代码:
// 颜色转换(24位BMP -> 目标格式)
if(vinfo.bits_per_pixel == 32) {col = (r << 16) | (g << 8) | b; // RGB888
} else {col = ((r>>3)<<11) | ((g>>2)<<5) | (b>>3); // RGB565
}
(3) 字符绘制 (
draw_word
)
点阵结构:
每字节代表8个水平像素点(MSB在最左)
数组排列:行优先存储
相关代码:
for(int j=0; j<h; j++) { // 行循环unsigned char byte = pword[i + j*w];for(int bit=0; bit<8; bit++) { // 位循环if(byte & 0x80) // 检测最高位draw_point(x+i*8+bit, y+j, color);byte <<= 1; // 移位检测下一位}
}
4相关函数
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
功能:建立内存映射
参数:
addr :映射的用户空间首地址
NULL:让操作系统自己分配用户空间
length:要映射的空间大小
prot: 操作权限
PROT_READ Pages may be read.
PROT_WRITE Pages may be written
flag : MAP_SHARED
fd:显示设备文件描述符
offset:偏移量
0:从显存开头映射
返回值:
成功:映射的用户空间首地址
失败:MAP_FAILED((void *)-1)
3、使用注意事项
1.权限问题:sudo chmod 666 /dev/fb0 # 避免权限错误
2.性能优化:
避免单点绘制(批量操作内存块)
使用双缓冲机制减少闪烁:
// 切换显示缓冲区 vinfo.yoffset = next_buffer * screen_height; ioctl(fb, FBIOPAN_DISPLAY, &vinfo);
3.常见问题处理:
花屏:检查像素格式是否匹配(RGB565 vs ARGB8888)
偏移错误:确认BMP文件头解析正确
内存泄漏:确保munmap配对mmap
4.framebuffer相关操作的封装函数
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include "framebuffer.h"
#include <math.h>void *pmem = NULL;
int fb;
struct fb_var_screeninfo vinfo;int init_fb(char *devname)
{//1. 打开显示设备(/dev/fb0)fb = open(devname, O_RDWR);if (-1 == fb){perror("open fb error");return -1;}//2. 获取显示设备相关参数(分辨率,像素格式)int ret = ioctl(fb, FBIOGET_VSCREENINFO, &vinfo);if (ret < 0){perror("ioctl error");return -1;}printf("xres = %d, yres = %d\n", vinfo.xres, vinfo.yres);printf("xres_virtual = %d, yres_virtual = %d\n", vinfo.xres_virtual, vinfo.yres_virtual);printf("bits_per_pixel = %d\n", vinfo.bits_per_pixel);//3. 建立显存空间和用户空间的内存映射size_t len = vinfo.xres_virtual * vinfo.yres_virtual * vinfo.bits_per_pixel/8;pmem = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fb, 0);if (pmem == MAP_FAILED){perror("mmap error");return -1;}return 0;
}int uninit_fb()
{//5. 解除映射关系//6. 关闭显示设备size_t len = vinfo.xres_virtual * vinfo.yres_virtual * vinfo.bits_per_pixel/8;munmap(pmem, len);close(fb);}//绘制一个点
void draw_point(int x, int y, unsigned int col)
{if (x >= vinfo.xres || y >= vinfo.yres){return ;}if (vinfo.bits_per_pixel == RGB_FMT_888){unsigned int *p = pmem;*(p+vinfo.xres_virtual*y+x) = col;}else if (vinfo.bits_per_pixel == RGB_FMT_565){unsigned short *p = pmem;*(p+vinfo.xres_virtual*y+x) = col;}
}//绘制一条线
void draw_x_line( int y, int x_begin, int x_end, unsigned int col)
{int i;for(i = x_begin; i < x_end; ++i){draw_point(i, y, col);}}void draw_y_line( int x, int y_begin, int y_end, unsigned int col)
{int i;for(i = y_begin; i < y_end; ++i){draw_point(x, i, col);}
}//绘制矩形
void draw_rect(int x_begin, int y_begin, int weight, int hight, unsigned int col )
{ draw_x_line(y_begin, x_begin, x_begin + weight, col);draw_y_line(x_begin, y_begin, y_begin + hight, col);draw_x_line(y_begin + hight, x_begin, x_begin + weight, col);draw_y_line(x_begin + weight, y_begin, y_begin + hight, col);
}//绘制圆
void draw_Circle(int x, int y, int r, unsigned int col)
{int x0 = 0;int y0 = 0;int si = 0;for (si = 0; si <= 360; si++){x0 = r * sin(PI * 2 / 360 * si) + x; y0 = r * cos(PI * 2 / 360 * si) + y;draw_point(x0, y0, col);}
}//获取bmp的文件头和图片头
int get_bmp_head_info(const char *bmpname, Bmp_file_head_t *pheadinfo, Bmp_info_t *pbmpinfo)
{FILE *fp = fopen(bmpname, "r");if (NULL == fp){perror("fopen error");return -1;}fread(pheadinfo, sizeof(Bmp_file_head_t), 1, fp);fread(pbmpinfo, sizeof(Bmp_info_t), 1, fp);fclose(fp);return 0;
}//绘制图片
void draw_bmp(int x, int y, char *bmpname)
{Bmp_file_head_t headinfo;Bmp_info_t bmpinfo;get_bmp_head_info(bmpname, &headinfo, &bmpinfo);int fd = open(bmpname, O_RDONLY);if (-1 == fd){perror("open bmp error");return ;}lseek(fd, 54, SEEK_SET);unsigned char *buff = malloc(bmpinfo.biHeight*bmpinfo.biWidth*bmpinfo.biBitCount/8);read(fd, buff, bmpinfo.biHeight*bmpinfo.biWidth*bmpinfo.biBitCount/8);close(fd);unsigned char *p = buff;unsigned char r, g, b;for (int j = bmpinfo.biHeight-1; j >= 0; j--){for (int i = 0; i < bmpinfo.biWidth; i++){b = *p;++p;g = *p;++p;r = *p;++p;if (vinfo.bits_per_pixel == RGB_FMT_888){unsigned int col = (r << 16) | (g << 8) | (b << 0);draw_point(i+x, j+y, col);}else if (vinfo.bits_per_pixel == RGB_FMT_565){unsigned short col = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);draw_point(i+x, j+y, col);}}}free(buff);}//绘制字符
void draw_word(int x, int y, unsigned char *pword, int w, int h, unsigned int col)
{for (int j = 0; j < h; j++){for (int i = 0; i < w; i++){unsigned char tmp = pword[i+j*w];for (int k = 0; k < 8; k++){if (tmp & 0x80){draw_point(x+i*8+k, y+j, col);}else{}tmp = tmp << 1;}}}
}