Linux编程 —— framebuffer

一、framebuffer概念

framebuffer:帧缓冲,帧缓存技术

                        Linux内核专门为图形化显示提供的一套应用程序接口。

 二、基本操作步骤

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

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;
}
#ifndef __FRAMEBUFFER_H__
#define __FRAMEBUFFER_H__#define RGB_FMT_888 32
#define RGB_FMT_565 16
#define PI   3.14
#pragma pack(1)//bmp文件相关信息
typedef struct tagBITMAPFILEHEADER {short    bfType;         // 文件类型标志int      bfSize;         // 文件大小,单位为字节short    bfReserved1;    // 保留字节short    bfReserved2;    // 保留字节int      bfOffBits;      // 数据偏移量,即实际图像数据开始的位置
}Bmp_file_head_t;
//bmp图像信息
typedef struct tagBITMAPINFOHEADER {int   biSize;         // BITMAPINFOHEADER的大小,单位为字节int    biWidth;        // 位图的宽度,单位为像素int    biHeight;       // 位图的高度,单位为像素short    biPlanes;       // 目标设备的位平面数,必须为1short    biBitCount;     // 每像素位数(颜色深度)int   biCompression;  // 图像压缩类型int   biSizeImage;    // 图像大小,单位为字节int    biXPelsPerMeter;// 水平分辨率,单位为像素/米int    biYPelsPerMeter;// 垂直分辨率,单位为像素/米int   biClrUsed;      // 实际使用颜色数int   biClrImportant; // 重要颜色数
}Bmp_info_t;
#pragma pack()extern int init_fb(char *devname);
extern int uninit_fb();
extern void draw_point(int x, int y, unsigned int col);
extern void draw_horizontal_line(int x1,int x2,int y,unsigned int col);
extern void draw_y_line(int x,int y,int ymax,unsigned int col);
extern void draw_face(int x1,int x2 ,int y1,int y2,int col);
extern void draw_Circle(int x, int y, int r, unsigned int col);
extern int get_bmp_head_info(const char *bmpname, Bmp_file_head_t *pheadinfo, Bmp_info_t *pbmpinfo);
extern void draw_bmp(int x, int y, char *bmpname);
extern void draw_word(int x, int y, unsigned char *pword, int w, int h, unsigned int col);
#endif

注:此为函数接口头文件

三、mmap函数接口

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

四、基本操作及算法           

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

注:虚拟界面视图大于实际视图         

①要保持起始点在视图范围内

②bits_per_pixel:表示每个像素所占用的位数(bit),它决定了图像的颜色深度和显示质量。数值越高,能够表示的颜色数量越多,图像的色彩表现越丰富。

RGB888:每个像素占8位,及一个字节 ,RGB888占3个字节,但在C中没有可以存储三字节的整形变量,它就会自动补为四字节,并将高位置零

RGB565:红色占5位,绿色占6位,蓝色占7位,  RGB565占2个字节

③pmem在建立内存映射时指向了显存,而我们不能直接对显存直接进行操做,所以定义相应的指针与pemp指向的地址相同,也可以理解为强转pemp,利用强转的pemp对显存所映射的用户空间进行操作   

2.画一条横线

void draw_horizontal_line(int x1,int x2,int y,unsigned int col)
{if(x1 >= vinfo.xres || x2 >=  vinfo.xres || y >= vinfo.yres){return ;}if(x1 > x2){int t = x1;x1 = x2;x2 = x1;}for(int i = 0;i <= x2 - x1 ; ++i ){if (vinfo.bits_per_pixel == RGB_FMT_888){unsigned int *p = pmem;*(p+vinfo.xres_virtual*y+(x1+i)) = col;}else if (vinfo.bits_per_pixel == RGB_FMT_565){unsigned short *p = pmem;*(p+vinfo.xres_virtual*y+(x1+i)) = col;}}
}

思想(点成面):画一条横线,横坐标改变,纵坐标不变,即绘制一条x1至x2的横线

3.画一条竖线

void draw_y_line(int x,int y,int ymax,unsigned int col)
{if(x >= vinfo.xres || y >=  vinfo.yres || ymax>= vinfo.yres){return ;}for(int i = 0;i <= ymax - y; ++i ){if (vinfo.bits_per_pixel == RGB_FMT_888){unsigned int *p = pmem;*(p+vinfo.xres_virtual*(y+i)+x) = col;}else if (vinfo.bits_per_pixel == RGB_FMT_565){unsigned short *p = pmem;*(p+vinfo.xres_virtual*(y+i)+x) = col;}}
}

4.绘制一个面 

void draw_face(int x1,int x2 ,int y1,int y2,int col)
{if(x1 > x2){int t = x1;x1 = x2;x2 = t;}if(y1 > y2){int t = y1;y1 = y2;y2 = t;}if(x1 >= vinfo.xres ||x2 >=vinfo.xres|| y1 >=  vinfo.yres || y2 >= vinfo.yres){return ;}int i = 0; if (vinfo.bits_per_pixel == RGB_FMT_888){for(i = 0;i < y2 - y1;++i){draw_horizontal_line(x1,x2,y1+i,col);draw_horizontal_line(x1,x2,y2+i,col);}       }else if (vinfo.bits_per_pixel == RGB_FMT_565){ for(i = 0;i < y2 - y1;++i){draw_horizontal_line(x1,x2,y1+i,col);draw_horizontal_line(x1,x2,y2+i,col);}}if (vinfo.bits_per_pixel == RGB_FMT_888){for(i = 0;i < x2 -x1;++i){   draw_y_line(x1 + 1,y1,y2,col);draw_y_line(x2 + 1,y1,y2,col);}}else if (vinfo.bits_per_pixel == RGB_FMT_565){for(i = 0;i < x2 -x1;++i){   draw_y_line(x1 + 1,y1,y2,col);draw_y_line(x2 + 1,y1,y2,col);}}
}

思想(线成面)

6.绘制一个圆 

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

思想:x,y为圆心坐标,圆周上任意一点为(x0,y0)

sin(PI * 2 / 360 * si) ,cos(PI * 2 / 360 * si) 是将角度转化为弧度

7,绘制一个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);}

                                                                                                                                                                                                                                                                          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)。

                                                                                                                                                             8.绘制一个字

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.pswp.cn/web/92959.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/92959.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

文件编辑html

<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>文件行内容编辑器</title><script src&…

具有熔断能力和活性探测的服务负载均衡解决方案

一、整体架构设计 1.核心组件 负载均衡器&#xff1a;负责选择可用的服务节点健康检查器&#xff1a;定期检测服务节点的可用性服务节点管理&#xff1a;维护所有可用节点的状态信息 2.负载均衡策略 轮询(Round Robin)随机(Random)加权轮询(Weighted Round Robin)最少连接(Leas…

技术演进中的开发沉思-62 DELPHI VCL系列:VCL下的设计模式

今天聊聊设计模式&#xff0c;当然这个章节目前仅限于DELPHI VCL,因为接下来梳理的Factory/Factory Method、Bootstrap 和 ForEach 这三种设计样例&#xff0c;看似独立&#xff0c;却在实际开发中相互配合&#xff0c;共同构建起高效、灵活的程序架构。在 DELPHI VCL 开发的技…

Docker 101:面向初学者的综合教程

掌握 Docker 已成为软件开发中的一项关键技能。本教程探讨了容器化的世界&#xff0c;包括其核心概念、优缺点&#xff0c;以及开始使用容器化的分步指南。 无论是 Docker 的新手&#xff0c;还是希望复习基础知识的更有经验的开发人员&#xff0c;本指南都能满足需求。 什么…

RTOS YAFFS

在 YAFFS (Yet Another Flash File System) 的语境中&#xff0c;“Check Point” 并不是一个标准的、核心的官方术语。它更可能是对 YAFFS 关键机制 Summary 或 Checkpointing 功能的非正式表述或理解偏差。其核心含义是指 YAFFS 在特定时刻保存文件系统关键元数据的状态&…

【SpringBoot系列-02】自动配置机制源码剖析

【SpringBoot系列-02】自动配置机制源码剖析 咱们天天用Spring Boot&#xff0c;一个SpringBootApplication注解扔进去&#xff0c;啥配置都不用写&#xff0c;项目就跑起来了。你有没有过这种疑惑&#xff1a;那些DispatcherServlet、DataSource是从哪冒出来的&#xff1f;今天…

51单片机-51单片机最小系统

本章概述思维导图&#xff1a;51单片机最小系统51单片机最小系统是51系列单片机&#xff08;如AT89C51、STC89C52等&#xff09;能够独立工作的最简电路配置&#xff0c;它为单片机提供了运行所需的基本条件。51单片机最小系统板是嵌入式系统开发的基础平台&#xff0c;集成了单…

git学习1

目录引入版本控制集中式和分布式版本控制git工作机制代码托管中心Git常用命令设置用户签名初始化本地库查看库状态add和提交版本穿梭git分支操作分支定义分支好处分支操作查看分支创建分支切换分支分支合并&#x1f495;✨&#x1fa77;合并冲突git团队协作团队内协作跨团队协作…

redis原理篇--Dict

Dict数据结构一、Redis字典的核心组件Redis字典由三部分构成&#xff1a;dictht&#xff08;哈希表&#xff09;&#xff1a;存储桶数组与元数据dictEntry&#xff08;哈希节点&#xff09;&#xff1a;存储键值对dict&#xff08;字典主体&#xff09;&#xff1a;包含双哈希表…

静态路由主备切换

在网络中&#xff0c;静态路由的主备切换是实现网络冗余的基础方案之一&#xff0c;通过配置不同优先级的静态路由&#xff0c;确保主用路径故障时&#xff0c;流量能自动切换到备用路径&#xff0c;提升网络可靠性。以下从知识讲解和实验配置两部分详细说明。一、静态路由主备…

PDF处理控件Aspose.PDF教程:在C#、Java、Python中快速缩小PDF

如果您的PDF太大&#xff0c;无法通过电子邮件发送&#xff0c;或者在线加载时间过长&#xff0c;您可以在几秒钟内缩小 PDF 大小。本教程介绍了借助Aspose.PDF使用 C#、Java 和 Python 编程快速缩小PDF的方法。 Aspose.PDF官方试用版下载 通过编程缩小 PDF 尺寸 如果您需要…

AWS EKS 常用命令大全:从基础管理到高级运维

前言 Amazon Elastic Kubernetes Service (EKS) 是 AWS 提供的托管 Kubernetes 服务,大大简化了 K8s 集群的部署和管理工作。作为 EKS 管理员或开发者,熟练掌握 kubectl 命令是日常工作的基础。本文将详细介绍 EKS 环境中常用的 kubectl 命令,涵盖集群管理、工作负载操作、…

GitHub Browser-Use 的部署失败记录:失败了,失败了。。。。

一、项目背景与核心作用 browser-use 是一个开源的浏览器自动化工具&#xff0c;通过集成 AI 智能体&#xff08;如 GPT、Claude、DeepSeek 等大型语言模型&#xff09;&#xff0c;实现用自然语言控制浏览器操作。其核心目标是 简化网页交互自动化&#xff0c;尤其适合复杂、…

调用springboot接口返回403,问题定位及总结

背景在一次与前端联调后端接口时前端返回接口返回状态码是403&#xff0c;前端返回说已经带了请求token。排查 查看后端控制台没有出现任何错误信息。自己postman手动调用接口&#xff0c;发现接口正常。仔细核对前端调用接口与postman请求的区别&#xff0c;没有发现任何问题。…

布隆过滤器原理分析、应用场景、与redis使用案例

一、核心结构与工作原理1.1 数据结构布隆过滤器由以下两部分组成&#xff1a;位数组&#xff08;Bit Array&#xff09;&#xff1a;一个长度为 m 的二进制数组&#xff0c;初始所有位为0。哈希函数组&#xff1a;k 个独立的哈希函数&#xff0c;每个函数将输入元素映射到位数组…

异步并发×编译性能:Dart爬虫的实战突围

Dart凭借其高效的异步并发模型、AOT编译性能和现代化的语法&#xff0c;正成为爬虫开发中值得关注的新选择。特别是对于Flutter应用开发者而言&#xff0c;Dart提供了一种"全栈同语言"的独特优势。 本文我将通过实战代码展示如何利用Dart的核心优势——包括基于Futur…

Day 8: 深度学习综合实战与进阶技术 - 从优化到部署的完整流程

Day 8: 深度学习综合实战与进阶技术 - 从优化到部署的完整流程 🎯 学习目标: 掌握深度学习模型优化、调试、迁移学习等工业级技能,能够构建高性能的深度学习应用 📚 核心概念概览 核心概念解释: 模型优化: 通过正则化、学习率调度等技术提升模型性能和泛化能力 为什么需…

特征工程--机器学习

1、特征工程1.1 概念特征工程&#xff08;Feature Engineering&#xff09;是机器学习项目中非常关键的一步&#xff0c;它是指通过领域知识来选择、创建或修改能够使机器学习模型更好地工作的特征&#xff08;即输入变量&#xff09;。特征工程的目标是提高模型的性能&#xf…

支持任意 MCP 协议的客户端

支持任意 MCP 协议的客户端&#xff08;如&#xff1a;Cursor、Claude、Cline&#xff09;可方便使用高德地图 MCP server。目前支持Streamable HTTP, SSE 和 Node.js I/O 三种接入方式(推荐用户使用Streamable HTTP)。 快速接入-MCP Server|高德地图API

【线性代数】目录

【线性代数】线性方程组与矩阵——&#xff08;1&#xff09;线性方程组与矩阵初步【线性代数】线性方程组与矩阵——行列式【线性代数】线性方程组与矩阵——&#xff08;2&#xff09;矩阵与线性方程组的解【线性代数】线性方程组与矩阵——&#xff08;3&#xff09;线性方程…