
目录一、所需标准头文件二、修复后完整无 Bug 源码三、分模块详细解析分段清晰、层级分明模块 1打开图片文件模块 2定义存储图片信息的变量模块 3读取图片宽度文件偏移 0x12模块 4读取图片高度文件偏移 0x16模块 5读取图片色深文件偏移 0x1C模块 6调试打印图片参数模块 7计算每行填充字节BMP 核心对齐规则模块 8动态分配像素内存增加容错判断模块 9读取全部像素数据到内存模块 10双层循环解析像素、绘制屏幕1. 循环结构2. 色彩读取规则3. 32 位颜色合成逻辑4. 屏幕坐标自动翻转5. 绘图与跳过填充模块 11资源释放杜绝资源泄漏四、函数整体执行流程总结一、所需标准头文件无需自定义common.h全部使用 Linux 系统原生头文件可直接编译#include unistd.h #include fcntl.h #include sys/types.h #include sys/stat.h #include stdlib.h #include stdio.h #include math.h头文件功能说明unistd.h文件光标移动lseek、读取read、关闭文件closefcntl.h文件打开open、只读模式宏O_RDONLYsys/types.h / sys/stat.hLinux 文件操作配套基础头文件stdlib.h动态内存分配malloc、内存释放freestdio.h控制台打印printf、错误提示perrormath.h绝对值计算函数abs()lcd.h自定义 LCD 驱动提供画点接口lcd_draw_point()二、修复后完整无 Bug 源码//显示一张bmp图片x0、y0代表图片左上角在LCD屏幕的坐标 void bmp_display(const char *bmp_file,int x0,int y0) { //打开BMP图片文件 int bmp_fd open(bmp_file,O_RDONLY); if(bmp_fd -1) { perror(open bmp failed); return ; } int width,height; short depth; unsigned char buf[4]; //读取图片宽度 lseek(bmp_fd,0x12,SEEK_SET); read(bmp_fd,buf,4); width (buf[3] 24) | (buf[2] 16) | (buf[1] 8) | buf[0]; //读取图片高度 lseek(bmp_fd,0x16,SEEK_SET); read(bmp_fd,buf,4); height (buf[3] 24) | (buf[2] 16) | (buf[1] 8) | buf[0]; //读取图片色深 lseek(bmp_fd,0x1c,SEEK_SET); read(bmp_fd,buf,2); depth (buf[1] 8) | buf[0]; //打印图片信息用于调试 printf(width : %d x height: %d depth:%d\n,width,height,depth); //计算行对齐填充字节 int valid_bytes_per_line; int laizi 0; int total_bytes_per_line; int total_bytes; valid_bytes_per_line abs(width) * (depth / 8); if(valid_bytes_per_line % 4) { laizi 4 - valid_bytes_per_line % 4; } total_bytes_per_line valid_bytes_per_line laizi; total_bytes total_bytes_per_line * abs(height); //分配像素内存并判断是否分配成功 unsigned char *pixel (unsigned char *)malloc(total_bytes); if(pixel NULL) { perror(malloc failed); close(bmp_fd); return; } //读取全部像素数据 lseek(bmp_fd,0x36,SEEK_SET); read(bmp_fd,pixel,total_bytes); //逐像素解析并绘制屏幕 unsigned char a,r,g,b; int color; int i 0; int x,y; for(y 0; y abs(height); y) { for(x 0; x abs(width); x) { b pixel[i]; g pixel[i]; r pixel[i]; if(depth 32) a pixel[i]; else a 0; color (a 24) | (r 16) | (g 8) | b; //换算屏幕坐标自动处理图片翻转 int x1 (width 0) ? (x x0) : (x0 abs(width) - 1 - x); int y1 (height 0) ? (y0 abs(height) - 1 - y) : (y y0); lcd_draw_point(x1,y1,color); } //跳过每行末尾无效填充字节 i laizi; } //释放内存、关闭文件回收全部资源 free(pixel); close(bmp_fd); }三、分模块详细解析分段清晰、层级分明模块 1打开图片文件int bmp_fd open(bmp_file,O_RDONLY); if(bmp_fd -1) { perror(open bmp failed); return ; }1. open(文件路径, O_RDONLY)以只读方式读取图片不会修改原图2. bmp_fd 文件描述符文件唯一标识后续所有文件操作都依靠它3. 异常判断文件不存在、权限不足时返回 - 1perror打印错误信息后直接退出函数。模块 2定义存储图片信息的变量int width,height; short depth; unsigned char buf[4];变量作用width图片横向像素总数负数代表图像需要水平翻转height图片纵向像素总数正数代表图像上下倒置存储depth色深仅支持 24 位无透明、32 位带透明通道buf[4]临时缓冲区读取文件头零散字节数据模块 3读取图片宽度文件偏移 0x12lseek(bmp_fd,0x12,SEEK_SET); read(bmp_fd,buf,4); width (buf[3] 24) | (buf[2] 16) | (buf[1] 8) | buf[0];1. lseek(fd, 偏移, SEEK_SET)将读取光标移动到文件第 0x12 字节位置该位置固定存放图片宽度2. BMP 为小端存储低位字节存在文件前面通过移位 或运算把 4 个单字节拼接为完整整数。模块 4读取图片高度文件偏移 0x16lseek(bmp_fd,0x16,SEEK_SET); read(bmp_fd,buf,4); height (buf[3] 24) | (buf[2] 16) | (buf[1] 8) | buf[0];1. 逻辑与读取宽度完全一致0x16 是文件规定存放高度的固定偏移2. height0图片上下颠倒存储绘图时需要反向矫正height0像素顺序正常。模块 5读取图片色深文件偏移 0x1Clseek(bmp_fd,0x1c,SEEK_SET); read(bmp_fd,buf,2); depth (buf[1] 8) | buf[0];1. 偏移 0x1C 存放 2 字节色深数据因此仅读取 2 个字节2. depth24仅 BGR 三色无透明效果depth32包含 Alpha 透明通道。模块 6调试打印图片参数printf(width : %d x height: %d depth:%d\n,width,height,depth);终端输出图片宽、高、色深快速校验文件头解析是否正常。模块 7计算每行填充字节BMP 核心对齐规则规则BMP 强制规定图片单行总字节必须是 4 的倍数不足则在行尾补充无意义填充字节绘图时必须跳过填充字节否则画面花屏。int valid_bytes_per_line; int laizi 0; int total_bytes_per_line; int total_bytes; valid_bytes_per_line abs(width) * (depth / 8); if(valid_bytes_per_line % 4) { laizi 4 - valid_bytes_per_line % 4; } total_bytes_per_line valid_bytes_per_line laizi; total_bytes total_bytes_per_line * abs(height);变量含义valid_bytes_per_line一行像素真实占用字节不含填充laizi需要补充的空白填充字节数量total_bytes_per_line单行完整字节像素字节 填充字节total_bytes整张图片全部数据总字节用于申请内存模块 8动态分配像素内存增加容错判断unsigned char *pixel (unsigned char *)malloc(total_bytes); if(pixel NULL) { perror(malloc failed); close(bmp_fd); return; }1. malloc 在堆内存开辟对应大小空间存储整张图片数据2. 内存不足时malloc返回 NULL若不判断会造成程序崩溃3. 分配失败流程打印错误 → 关闭打开的文件 → 退出函数避免资源浪费。模块 9读取全部像素数据到内存lseek(bmp_fd,0x36,SEEK_SET); read(bmp_fd,pixel,total_bytes);标准 24/32 位 BMP 文件头固定 54 字节对应偏移 0x36从该位置开始才是像素色彩数据注意偏移 0x0A 存储像素起始偏移值无调色板图像该值等于 0x36带调色板图像必须读取此字段不可硬编码 0x36一次性读取全部数据存入内存减少硬盘 IO提升绘图速度。模块 10双层循环解析像素、绘制屏幕unsigned char a,r,g,b; int color; int i 0; int x,y; for(y 0; y abs(height); y) { for(x 0; x abs(width); x) { b pixel[i]; g pixel[i]; r pixel[i]; if(depth 32) a pixel[i]; else a 0; color (a 24) | (r 16) | (g 8) | b; int x1 (width 0) ? (x x0) : (x0 abs(width) - 1 - x); int y1 (height 0) ? (y0 abs(height) - 1 - y) : (y y0); lcd_draw_point(x1,y1,color); } i laizi; }1. 循环结构外层for(y)遍历图片每一行内层for(x)遍历当前行内每一个像素点2. 色彩读取规则BMP 像素存储顺序固定蓝 (B) → 绿 (G) → 红 (R)24 位图无透明通道a0完全不透明32 位图读取额外 1 字节 Alpha 透明值3. 32 位颜色合成逻辑color (a24) | (r16) | (g8) | b;最高 8 位透明通道 A次高 8 位红色 R次低 8 位绿色 G最低 8 位蓝色 B4. 屏幕坐标自动翻转x0、y0图片左上角在 LCD 屏幕的起始坐标水平width0 正向绘制width0 水平镜像垂直height0 上下反转矫正height0 正常绘制5. 绘图与跳过填充lcd_draw_point(x1,y1,color)在屏幕对应坐标画出像素i laizi一行像素绘制完成下标跳过行尾填充字节读取下一行有效像素。模块 11资源释放杜绝资源泄漏free(pixel); close(bmp_fd);1. free(pixel)释放 malloc 开辟的图片内存防止长期运行内存溢出2. close(bmp_fd)关闭图片文件释放文件描述符避免耗尽系统文件句柄。四、函数整体执行流程总结以只读模式打开 BMP 图片打开失败直接退出移动文件光标读取文件头固定偏移解析图片宽、高、色深根据 BMP4 字节对齐规则计算每行填充字节数量动态分配内存缓冲区内存分配失败则关闭文件退出光标跳转至像素起始位置一次性读取整张图片像素数据双层循环逐像素拆分 BGR (A) 色彩分量根据宽高正负自动矫正图像翻转换算屏幕真实坐标调用 LCD 画点函数逐点渲染图片单行绘制完成跳过填充字节循环绘制下一行绘图结束释放内存、关闭文件回收全部系统资源。