03-基于GEC6818开发板实现BMP图片的加载——实例分析

03-基于GEC6818开发板实现加载一张图片

实现基于GEC6818开发板实现加载一张BMP文件。其中详细解析了一张BMP格式图的内容。
其他相关GEC6818开发板的内容可以参考
01-基于粤嵌GEC6818实现屏幕的显示固定颜色进行自动切换
02-基于GEC6818开发板的画正方形、画圆的操作——使用mmap映射提高效率

文章目录

  • 03-基于GEC6818开发板实现加载一张图片
    • 一、 bmp图片的格式内容
      • 1.1 BITMAP文件头
      • 1.2 DIB头
      • 1.3 调色板(颜色数值组)
      • 1.4 像素数组
    • 二、 实例练习
      • 2.1加载一张图片实现代码
      • 2.2 进阶:对于图片大小不是800*480的图片进行轮播

一、 bmp图片的格式内容

我们要加载一张图片就必须知道这张图片的一些特定格式,并获取其中的像素数组。

那么BMP文件主要由四部分组成:BITMAP文件头,DIB头,调色板(颜色数值组),像素数组。

1.1 BITMAP文件头

在这里插入图片描述

文件头时表征这个文件是什么格式,例如我们下面代码中会提及的,判断这个图片是不是真的BMP文件,则可以

 //判断是否为真的BMP文件unsigned char buf[2];read(fd,buf,2);if(buf[0]!= 0x42 || buf[1]!= 0x4d)//若果不是B M 的ASCII码{printf("NOT BMP\n");return;}

1.2 DIB头

在这里插入图片描述

这一部分也非常重要,因为其中包含了很多图片的基础信息,比如图片的宽度,高度,色深以及图片的大小等,这些都有助于我们后续对这个文件进行进一步的操作。

 //读取数据int width,height,depth;//读取宽度,将偏移量偏移到宽度lseek(fd,0x12,SEEK_SET);read(fd,&width,4);//读取四个字节read(fd,&height,4);//高度lseek(fd,0x1c,SEEK_SET);read(fd,&depth,4);

1.3 调色板(颜色数值组)

在这里插入图片描述

1.4 像素数组

在这里插入图片描述

二、 实例练习

2.1加载一张图片实现代码

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <math.h> int lcd_fd = -1; // 全局的lcd描述符
unsigned int* plcd = NULL;void lcdinit() {lcd_fd = open("/dev/fb0", O_RDWR);if (-1 == lcd_fd) {perror("open fb0 error");exit(1);}plcd = mmap(NULL, 800 * 480 * 4, PROT_READ | PROT_WRITE, MAP_SHARED, lcd_fd, 0);if (plcd == MAP_FAILED) {perror("mmap error");return;}
}void lcd_destory() {munmap(plcd, 800 * 480 * 4);close(lcd_fd);
}void point(int x, int y, unsigned int color) {if (x >= 0 && x < 800 && y >= 0 && y < 480) {*(plcd + y * 800 + x) = color;}
}void display_sql(int w, int h, int x0, int y0, int color) {int x, y;for (y = 0; y < h; y++) {for (x = 0; x < w; x++) {point(x + x0, y + y0, color);}}
}
//添加背景颜色-color
void display_bgm(int color) {int w =800,h=480;int x, y;for (y = 0; y < h; y++) {for (x = 0; x < w; x++) {point(x , y , color);}}
}void display_bmp(const char* filename,int x0,int y0)
{//打开文件int fd = open(filename, O_RDONLY);if(-1 == fd){perror("open bmp error");return;}//判断是否为真的BMP文件unsigned char buf[2];read(fd,buf,2);if(buf[0]!= 0x42 || buf[1]!= 0x4d)//若果不是B M 的ASCII码{printf("NOT BMP\n");return;}//读取数据int width,height,depth;//读取宽度,将偏移量偏移到宽度lseek(fd,0x12,SEEK_SET);read(fd,&width,4);//读取四个字节read(fd,&height,4);//高度lseek(fd,0x1c,SEEK_SET);read(fd,&depth,4);//只支持色深24和32if(!(depth == 24 || depth == 32)){printf("NOT Support!\n");return;}printf("width = %d height = %d depth = %d ", width,height,depth);//4.获取像素数组int line_valid_bytes = abs(width)*depth/8;//一行有效字节数int line_bytes;//一行总字节数=有效字节数+赖子数 int laizi = 0;if(line_valid_bytes%4){laizi = 4-line_valid_bytes%4;}line_bytes = line_valid_bytes + laizi;int total_bytes = line_bytes*abs(height);//整个像素数组的大小
//开辟一块动态内存unsigned char *piexl = (unsigned char *)malloc(total_bytes);    //用完后需要释放内存lseek(fd,54,SEEK_SET);read(fd,piexl,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++){//a r g b 0xargb 小端模式  b g r ab = piexl[i++];g = piexl[i++];r = piexl[i++];if(depth == 32){a = piexl[i++];}else{a = 0;//不透明}color=(a<<24)|(r<<16)|(g<<8)|(b);//在屏幕对应的位置显示point(width>0?x0+x:x0+abs(width)-x-1, height>0?y0+abs(height)-y-1:y0+y,color);}//每一行的末尾 有可能填充几个赖子i += laizi;}//释放内存free(piexl);//关闭文件close(fd);
}int main() {lcdinit();display_bgm(0x000000);display_bmp("picture.bmp",0,0);lcd_destory();return 0;
}

上面代码中存在一段需要好好理解的一段

int line_valid_bytes = abs(width)*depth/8;//一行有效字节数int line_bytes;//一行总字节数=有效字节数+赖子数 int laizi = 0;if(line_valid_bytes%4){laizi = 4-line_valid_bytes%4;}line_bytes = line_valid_bytes + laizi;int total_bytes = line_bytes*abs(height);

这段代码计算了与BMP图像相关的字节信息。具体来说,它涉及到了BMP图像中每一行的数据存储方式,以及为什么需要计算和使用这些值。

以下是代码中各部分的解释:

  1. line_valid_bytes = abs(width) * depth / 8;

    • width 表示图像的宽度。
    • depth 表示每个像素的位深度,通常为24(表示RGB,每个颜色通道8位)或32(带有额外的透明度通道)。
    • 这一行计算了每一行的有效字节数。例如,如果图像的宽度是800像素,深度是24位,则每一行需要 800 * 3 = 2400 字节来存储数据。
  2. laizi = 0;

    • 这是一个中文词汇“赖子”的拼音,通常用于描述不完全的部分或余数。
  3. if(line_valid_bytes % 4)

    • 这里检查有效字节数是否可以被4整除。在BMP文件格式中,每一行的数据存储通常会在每行结束时填充到4字节的倍数。如果不能整除,就需要在每一行的末尾添加一些额外的字节(通常为0)使其达到4字节的倍数。
  4. laizi = 4 - line_valid_bytes % 4;

    • 如果line_valid_bytes不能被4整除,laizi会被设置为使得总字节数达到4字节倍数所需的字节数。
  5. line_bytes = line_valid_bytes + laizi;

    • 计算了每一行总共所需的字节数。
  6. int total_bytes = line_bytes * abs(height);

    • 这里计算了整个BMP图像所需的总字节数。它是每一行总字节数乘以图像的高度。

总的来说,这段代码的目的是为了正确地计算BMP图像在内存中的字节布局。由于BMP图像要求每一行的数据存储都必须是4字节的倍数,所以需要进行这样的计算来确保数据的正确存储和访问。

2.2 进阶:对于图片大小不是800*480的图片进行轮播

因为我们的LCD的大小是800*480,但是我们的图片的分辨率不是完全满足这个分辨率,那么就需要对图片进行居中显示,那么就只需要设置一定的偏移量就可以了.可以直接加入下面一段代码进去即可。

  //处理居中的情况if(width<800||height<480){x0 = (int)(800-width)/2;y0 = (int)(480-height)/2;}

具体实现代码

 #include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <math.h> int lcd_fd = -1; // 全局的lcd描述符
unsigned int* plcd = NULL;void lcdinit() {lcd_fd = open("/dev/fb0", O_RDWR);if (-1 == lcd_fd) {perror("open fb0 error");exit(1);}plcd = mmap(NULL, 800 * 480 * 4, PROT_READ | PROT_WRITE, MAP_SHARED, lcd_fd, 0);if (plcd == MAP_FAILED) {perror("mmap error");return;}
}void lcd_destory() {munmap(plcd, 800 * 480 * 4);close(lcd_fd);
}void point(int x, int y, unsigned int color) {if (x >= 0 && x < 800 && y >= 0 && y < 480) {*(plcd + y * 800 + x) = color;}
}
//添加背景颜色-color
void display_bgm(int color) {int w =800,h=480;int x, y;for (y = 0; y < h; y++) {for (x = 0; x < w; x++) {point(x , y , color);}}
}void display_mid(const char* filename)
{int x0,y0;//打开文件int fd = open(filename, O_RDONLY);if(-1 == fd){perror("open bmp error");return;}//判断是否为真的BMP文件unsigned char buf[2];read(fd,buf,2);if(buf[0]!= 0x42 || buf[1]!= 0x4d)//若果不是B M 的ASCII码{printf("NOT BMP\n");return;}//读取数据int width,height,depth;//读取宽度,将偏移量偏移到宽度lseek(fd,0x12,SEEK_SET);read(fd,&width,4);//读取四个字节read(fd,&height,4);//高度lseek(fd,0x1c,SEEK_SET);read(fd,&depth,4);//只支持色深24和32if(!(depth == 24 || depth == 32)){printf("NOT Support!\n");return;}printf("width = %d height = %d depth = %d ", width,height,depth);//处理居中的情况if(width<800||height<480){x0 = (int)(800-width)/2;y0 = (int)(480-height)/2;}//4.获取像素数组int line_valid_bytes = abs(width)*depth/8;//一行有效字节数int line_bytes;//一行总字节数=有效字节数+赖子数 int laizi = 0;if(line_valid_bytes%4){laizi = 4-line_valid_bytes%4;}line_bytes = line_valid_bytes + laizi;int total_bytes = line_bytes*abs(height);//整个像素数组的大小//开辟一块动态内存unsigned char *piexl = (unsigned char *)malloc(total_bytes);    //用完后需要释放内存lseek(fd,54,SEEK_SET);read(fd,piexl,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++){//a r g b 0xargb 小端模式  b g r ab = piexl[i++];g = piexl[i++];r = piexl[i++];if(depth == 32)//32 色的有透明度,但是对24位的来说无所谓这个a的都无效{a = piexl[i++];}else{a = 0;//不透明}color=(a<<24)|(r<<16)|(g<<8)|(b);//在屏幕对应的位置显示point(width>0?x0+x:x0+abs(width)-x-1, height>0?y0+abs(height)-y-1:y0+y,color);}//每一行的末尾 有可能填充几个赖子i += laizi;}//释放内存free(piexl);//关闭文件close(fd);
}int main() {lcdinit();//display_bgm(0x000000);const char* images[] = {"1.bmp","2.bmp","3.bmp"};int num_images = sizeof(images) / sizeof(images[0]);int current_image_index = 0;while(1){   display_mid(images[current_image_index]);sleep(2);display_bgm(0xFFFFFF);current_image_index = (current_image_index + 1) % num_images; // 切换到下一张图片}lcd_destory();return 0;
}

居中图片的显示
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/239649.shtml

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

相关文章

项目应用多级缓存示例

前不久做的一个项目&#xff0c;需要在前端实时展示硬件设备的数据。设备很多&#xff0c;并且每个设备的数据也很多&#xff0c;总之就是数据很多。同时&#xff0c;设备的刷新频率很快&#xff0c;需要每2秒读取一遍数据。 问题来了&#xff0c;我们如何读取数据&#xff0c…

74hc138电路图汇总分析

74hc138大家应该比较熟悉&#xff0c;关于它的应用电路你了解多少呢&#xff1f;它在电路中是一个什么样的角色定位呢&#xff1f;本文主要为你介绍关于74hc138电路图汇总分析以及74hc138在电路中的作用。 74hc138 74HC138是一款高速CMOS器件&#xff0c;74HC138引脚兼容低功…

【计算机视觉中的多视图几何系列】深入浅出理解针孔相机模型

温故而知新&#xff0c;可以为师矣&#xff01; 一、参考资料 《计算机视觉中的多视图几何-第五章》-Richard Hartley, Andrew Zisserman. 二、针孔模型相关介绍 1. 重要概念 1.1 投影中心/摄像机中心/光心 投影中心称为摄像机中心&#xff0c;也称为光心。投影中心位于一…

spring aop实际开发中怎么用,Spring Boot整合AOP,spring boot加spring mvc一起使用aop,项目中使用aop

前言&#xff1a;本文不介绍 AOP 的基本概念、动态代理方式实现 AOP&#xff0c;以及 Spring 框架去实现 AOP。本文重点介绍 Spring Boot 项目中如何使用 AOP&#xff0c;也就是实际项目开发中如何使用 AOP 去实现相关功能。 如果有需要了解 AOP 的概念、动态代理实现 AOP 的&…

web架构师编辑器内容-改进字体下拉菜单

前面说到我们可以通过面板配置来更新画布上面的一些属性&#xff0c;如果我们有这样一个需求&#xff1a;在右侧面板配置里面需要查看字体的样式效果我们应该怎么做呢&#xff1f; 我们一开始字体的渲染&#xff1a; const fontFamilyArr [{value: "SimSun","…

vue事件绑定

vue提供了v-on事件绑定指令&#xff0c;用来辅助程序员为DOM元素绑定事件监听&#xff0c;语法格式如下&#xff1a; v-on&#xff1a;指令可以简写为 注意&#xff1a;原生DOM对象有onclick&#xff0c;oninput&#xff0c;onkeyup等原生事件&#xff0c;替换为vue的事件绑定…

PYQT的使用入门

上一章节&#xff1a;VSCode安装PYQT5-CSDN博客 vscode导航栏右键&#xff0c;新建ui文件

<软考高项备考>《论文专题 - 23 整合管理(1) 》

1 论文基础情况 1.1 各过程写作要点 过程定义、作用写作要点、思路制定项目章程制定项目章程是编写一份正式批准项目并授权项目经理在项目活动中使用组织资源的文件的过程。作用:①明确项目与组织战略目标之间的直接联系&#xff1b;②确立项目的正式地位;③展示组织对项目的…

【Netty】编解码器

目录 Java的编解码Netty编解码器概念解码器(Decoder)编码器(Encoder)编码解码器Codec Java的编解码 编码&#xff08;Encode&#xff09;称为序列化&#xff0c; 它将对象序列化为字节数组&#xff0c;用于网络传输、数据持久化或者其它用途。 解码&#xff08;Decode&#x…

Python量化投资——金融数据最佳实践: 使用qteasy+tushare搭建本地金融数据仓库并定期批量更新【附源码】

用qteasytushare实现金融数据本地化存储及访问 目的什么是qteasy什么是tushare为什么要本地化使用qteasy创建本地数据仓库qteasy支持的几种本地化仓库类型配置本地数据仓库配置tushare 的API token 配置本地数据源 —— 用MySQL数据库作为本地数据源下载金融历史数据 数据的定期…

基于多反应堆的高并发服务器【C/C++/Reactor】(中)

在这篇文章中虽然实现了能够和多客户端建立连接&#xff0c;并且同时和多个客户端进行通信。 基于多反应堆的高并发服务器【C/C/Reactor】&#xff08;上&#xff09;-CSDN博客https://blog.csdn.net/weixin_41987016/article/details/135141316?spm1001.2014.3001.5501但是有…

SuperMap Hi-Fi 3D SDK for Unity基础开发教程

作者&#xff1a;kele 一、背景 众所周知&#xff0c;游戏引擎&#xff08;Unity&#xff09;功能强大&#xff0c;可以做出很多炫酷的游戏和动画效果&#xff0c;这部分功能的实现往往不仅仅是靠可视化界面就能够实现的&#xff0c;还需要代码开发。SuperMap Hi-Fi SDKS for …

清风数学建模笔记-插值算法

内容&#xff1a;插值算法 概念&#xff1a; 二.种类 1.牛顿插值法&#xff0c;拉格朗日插值法&#xff0c;两者容易出现龙格现象 2.分段线性插值&#xff1a;与上面两种相比要更好一些,原理是两线之间构成一条直线&#xff0c;在这条直线上插值&#xff0c;除此之外还有分段…

读算法霸权笔记01_数学杀伤性武器

1. 数学应用助推数据经济&#xff0c;但这些应用的建立是基于不可靠的人类所做的选择 1.1. 房地产危机&#xff0c;大型金融机构倒闭&#xff0c;失业率上升&#xff0c;在幕后运用着神奇公式的数学家们成为这些灾难的帮凶 1.2. 数学逐渐不再关注全球金融市场动态&#xff0c…

用Python处理PDF:拆分与合并PDF文档

PDF文档在信息共享和数据保存方面被广泛使用&#xff0c;处理PDF文档也成为常见需求。其中&#xff0c;合并和拆分PDF文档能够帮助我们更有效地管理PDF文档&#xff0c;使文档内容分布更合理。通过合并&#xff0c;可以将相关文档整合成一个文件&#xff0c;以便更好地组织和提…

LeetCode394.字符串解码

这道题有点像我之前写过的一道题有效的括号&#xff08;不只是栈&#xff09;-CSDN博客 但是比那道题要难&#xff0c;但用的方法是一样的&#xff0c;就是用栈的先进后出进行括号匹配&#xff0c;所以有写过之前那道题&#xff0c;这道题按照这个思路走我就写出了如下屎山代码…

使用宝塔面板部署前端项目到服务器

目录 文章目录 前言 一、第一步&#xff1a;创建文件夹 二、第二步&#xff1a;部署前端项目 三、第三步&#xff1a;打开防火墙 文章目录 前言第一步&#xff1a;创建文件夹第二步&#xff1a;部署前端项目第三步&#xff1a;打开防火墙总结 前言 在此之前&#xff0c;我…

智能优化算法应用:基于鹰栖息算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于鹰栖息算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于鹰栖息算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.鹰栖息算法4.实验参数设定5.算法结果6.参考文…

DHorse v1.5.0 发布,基于 k8s 的发布平台

版本说明 新增特性 支持同一机器部署多个DHorse服务&#xff1b;支持Next、.NET应用部署&#xff1b;优化Node、Nuxt应用构建和部署的性能&#xff1b;默认使用fabric8客户端与k8s集群交互&#xff0c;可以通过指定参数-Dkubernetes-clientofficial切回到k8s官方客户端&#…

亚马逊推出 Graviton4:具有 536.7 GBps 内存带宽的 96 核 ARM CPU

如今&#xff0c;许多云服务提供商都设计自己的芯片&#xff0c;但亚马逊网络服务 (AWS) 开始领先于竞争对手&#xff0c;目前其子公司 Annapurna Labs 开发的处理器可以与 AMD 和英特尔的处理器竞争。本周&#xff0c;AWS 推出了 Graviton4 SoC&#xff0c;这是一款基于 ARM 的…