当前位置: 首页 > news >正文

Linux高级编程-framebuffer

1.framebuffer概念

UI技术:User interface  

framebuffer:帧缓冲、帧缓存技术
Linux内核专门为图形化显示提供的一套应用程序接口。

2.基本操作

(1)操作步骤

1. 打开显示设备(/dev/fb0)
2. 获取显示设备相关参数(分辨率,像素格式)---》ioctl
3. 建立显存空间和用户空间的内存映射
4. 向映射的用户空间写入RGB颜色值
5. 解除映射关系
6. 关闭显示设备

(2)函数接口

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;
}

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)

int uninit_fb()
{//5. 解除映射关系//6. 关闭显示设备size_t len = vinfo.xres_virtual * vinfo.yres_virtual * vinfo.bits_per_pixel/8;munmap(pmem, len);close(fb);}

3.基本算法实现

(1)画一个点

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;}
}

算法思想:

边界检查:首先检查坐标 (x, y) 是否在屏幕有效范围内(x < vinfo.xres 且 y < vinfo.yres),若越界则直接返回。

像素格式处理:

若为 32位RGB888格式(RGB_FMT_888):

将显存指针 pmem 转换为 unsigned int* 类型。

计算目标像素的线性地址:pmem + vinfo.xres_virtual * y + x。

将颜色值 col 直接写入该地址。

若为 16位RGB565格式(RGB_FMT_565):

将显存指针 pmem 转换为 unsigned short* 类型。

计算目标像素的线性地址:pmem + vinfo.xres_virtual * y + x。

将颜色值 col(需确保是16位)写入该地址。

(2)画一条横线

void draw_x_line(int x, int y, int end, unsigned int col)
{for(int i = 0; i < end; ++i){draw_point((x + i), y, col);}
}

算法思想:

水平线绘制:从起点 (x, y) 开始,向右连续绘制 end 个像素。

循环控制:

初始化计数器 i = 0。

循环条件 i < end,每次迭代调用 draw_point(x + i, y, col) 绘制一个像素。

循环结束后,形成一条从 (x, y) 到 (x + end - 1, y) 的水平线段。

(3)画一条竖线

void draw_y_line(int x, int y, int end, unsigned int col)
{for(int i = 0; i < end; ++i){draw_point(x, (y + i), col);}
}

算法思想:

垂直线绘制:从起点 (x, y) 开始,向下连续绘制 end 个像素。

循环控制:

初始化计数器 i = 0。

循环条件 i < end,每次迭代调用 draw_point(x, y + i, col) 绘制一个像素。

循环结束后,形成一条从 (x, y) 到 (x, y + end - 1) 的垂直线段。

(4)画一个矩形

void draw_rect(int x, int y, int weight, int high, unsigned int col)
{draw_x_line(x, y, weight, col);draw_x_line(x, (y + high), weight, col);draw_y_line(x, y, high, col);draw_y_line((x + weight), y, high, col);
}

算法思想:

矩形边框绘制:通过组合四条边完成矩形绘制:

上边:调用 draw_x_line(x, y, weight, col)。

下边:调用 draw_x_line(x, y + high, weight, col)。

左边:调用 draw_y_line(x, y, high, col)。

右边:调用 draw_y_line(x + weight, y, high, col)。

注意:

此函数仅绘制矩形边框,不填充内部。

(5)画一个圆

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);}
}

算法思想:

参数化圆绘制:

遍历角度 si 从 0° 到 360°,步长为 1°。

对每个角度 si:

计算圆上点的坐标:

x0 = r * sin(θ) + x(θ = PI * 2 / 360 * si 弧度)。

y0 = r * cos(θ) + y。

调用 draw_point(x0, y0, col) 绘制单个像素。

效果:

通过离散点近似形成一个圆形,精度取决于角度步长。

(6)画一个图

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);}

get_bmp_head_info(const char *bmpname, Bmp_file_head_t *pheadinfo, Bmp_info_t *pbmpinfo)

算法思想:

文件操作:

以只读模式打开BMP文件,失败时返回错误。

头信息读取:

使用 fread 读取BMP文件头(Bmp_file_head_t)到 pheadinfo。

继续读取BMP信息头(Bmp_info_t)到 pbmpinfo。

资源释放:关闭文件句柄,返回成功状态。

draw_bmp(int x, int y, char *bmpname)

算法思想:

头信息解析:调用 get_bmp_head_info 获取BMP的宽、高、位深等信息。

像素数据加载:

打开文件并跳过54字节的文件头和信息头。

根据BMP信息头中的图像尺寸和位深(如 biWidth × biHeight × biBitCount/8),分配缓冲区 buff 并读取像素数据。

像素绘制:

BMP像素顺序:BMP文件存储顺序为从下到上,需反向遍历行(j 从 biHeight-1 到 0)。

颜色转换:

对于 RGB888格式屏幕:将BGR像素数据(BMP格式)转换为RGB888,合并为 unsigned int 后调用 draw_point。

对于 RGB565格式屏幕:将RGB分量压缩为5-6-5位,合并为 unsigned short。

绘制每个像素到屏幕坐标 (x + i, y + j)。

(7)在图中加入字(使用字模)

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;}}}
}

算法思想:

字模数据解析:

字模数据 pword 为 w × h 的字节数组,每个字节表示8个水平像素点(1位1像素)。

逐行逐像素绘制:

外层循环遍历行(j 从 0 到 h-1)。

内层循环遍历列(i 从 0 到 w-1):

获取当前字节 tmp = pword[i + j * w]。

对字节的8个位(从高位到低位):

若某位为 1,调用 draw_point(x + i*8 + k, y + j, col) 绘制像素。

若为 0,跳过(透明或背景)。

效果:

将1位字模数据按指定颜色展开为8倍宽度的字符图形。

http://www.dtcms.com/a/329417.html

相关文章:

  • 华为悦盒EC6108V9-1+4G版-盒子有【蓝色USB接口】的特殊刷机说明
  • 数据分析全景:从数据到决策的完整链路与核心要义
  • 《Python学习之基础语法2:掌握程序流程控制的艺术》
  • 【分布式 ID】一文详解美团 Leaf
  • TCP Socket 编程实战:实现简易英译汉服务
  • 函数扇入数(Fan-in)
  • NAT技术、代理服务器+网络通信各层协议
  • transforms的使用 小土堆pytorch记录
  • 深度学习流体力学:基于PyTorch的物理信息神经网络(PINN)完整实现
  • PyTorch Tensor完全指南:深度学习数据操作的核心艺术
  • C++编程学习(第21天)
  • LeetCode 分类刷题:1004. 最大连续1的个数 III
  • 【Linux】常用命令(三)
  • 智慧养老丨实用科普+避坑指南:科技如何让晚年生活更安全舒适?
  • 编译 C++ 程序时提示:fatal error: ‘json/json.h‘ file not found
  • 使用 HTML5 Canvas 打造炫酷的数字时钟动画
  • Linux基本操作命令
  • Go 语言函数详解:从基础到高阶的行为逻辑构建
  • 基于SD-WAN的医疗工厂弱电智能化机房方案:架构设计与实践
  • 具有熔断能力和活性探测的服务负载均衡解决方案
  • Go与Python爬虫实战对比:从开发效率到性能瓶颈的深度解析
  • 【车联网kafka】Kafka核心架构与实战经验(第四篇)
  • docker镜像状态监控
  • 磁悬浮轴承转子设计避坑指南:深度解析核心要点与高可靠性策略
  • 【网络运维】Playbook进阶: 管理变量
  • 如何在 Spring Boot 中设计和返回树形结构的组织和部门信息
  • [AI React Web] E2B沙箱 | WebGPU | 组件树 | 智能重构 | 架构异味检测
  • [AI React Web] 包与依赖管理 | `axios`库 | `framer-motion`库
  • Matlab(4)
  • STM32H5 的 PB14 引脚被意外拉低的问题解析