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和32
    if(!(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 a
   b = 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和32
    if(!(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 a
   b = 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;
}

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LTO-3 磁带机种草终于是用上了

跑来跑去&#xff0c;买了不少配件&#xff0c;终于是把这磁带机给用上了&#xff0c;已经备份好了300 多 GB 的数据。 我们用了 NAS 的数据压缩功能&#xff0c;把需要备份的文件用 NAS 压缩成一个 Zip 文件&#xff0c;如果你可以 tar 的话也行。 这样传输速度更快&#xf…

【即插即用篇】YOLOv8改进实战 | 引入 Involution(内卷),用于视觉识别的新一代神经网络!涨点神器!

YOLOv8专栏导航:点击此处跳转 前言 YOLOv8 是由 YOLOv5 的发布者 Ultralytics 发布的最新版本的 YOLO。它可用于对象检测、分割、分类任务以及大型数据集的学习,并且可以在包括 CPU 和 GPU 在内的各种硬件上执行。 YOLOv8是一种尖端的、最先进的 (SOTA) 模型,它建立在以前成…

金蝶Apusic应用服务器 loadTree JNDI注入漏洞复现(QVD-2023-48297)

0x01 产品简介 金蝶Apusic应用服务器是一款企业级应用服务器,支持Java EE技术,适用于各种商业环境。 0x02 漏洞概述 由于金蝶Apusic应用服务器权限验证不当,导致攻击者可以向loadTree接口执行JNDI注入,造成远程代码执行漏洞。利用该漏洞需低版本JDK。(漏洞比较旧,8月份…

Linux ContOS7 日志管理(rsyslog)

目录 01. rsyslog 记录日志程序 02.日志文件 03.日志等级 Linux 日志文件是记录 Linux 系统运行信息的文件。它们类似于人类的日记&#xff0c;记录了系统的各种活动&#xff0c;如用户登录、进程启动、错误消息等。 Linux 日志文件通常存储在 /var/log/ 目录中。该目录包含…

STM32G4x FLASH 读写配置结构体(LL库下使用)

主要工作就是把HAL的超时用LL库延时替代&#xff0c;保留了中断擦写模式、轮询等待擦写&#xff0c;我已经验证了部分。 笔者用的芯片为STM32G473CBT6 128KB Flash&#xff0c;开环环境为CUBEMXMDK5.32&#xff0c;因为G4已经没有标准库了&#xff0c;笔者还是习惯使用标准库的…

3.[BUUCTF HCTF 2018]WarmUp1

1.看题目提示分析题目内容 盲猜一波~ &#xff1a; 是关于PHP代码审计的 2.打开链接&#xff0c;分析题目 给你提示了我们访问source.php来看一下 大boss出现&#xff0c;开始详细手撕~ 3.手撕PHP代码&#xff08;代码审计&#xff09; 本人是小白&#xff0c;所以第一步&…

Python 将RTF文件转为Word 、PDF、HTML

RTF也称富文本格式&#xff0c;是一种具有良好兼容性的文档格式&#xff0c;可以在不同的操作系统和应用程序之间进行交换和共享。有时出于不同项目的需求&#xff0c;我们可能需要将RTF文件转为其他格式。本文将介如何通过简单的Python代码将RTF文件转换为Word Doc/Docx、PDF、…

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

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

XML简介 (EXtensible Markup Language)

XML简介 (EXtensible Markup Language) 可扩展标记语言 特点 XML与操作系统、编程语言的开发平台无关实现不同系统之间的数据交换 作用 数据交互配置应用程序和网站Ajax基石 XML标签 XML文档内容由一系列标签元素组成 <元素名 属性名"属性值">元素内容&l…

Echarts饼图tooltip渐变色,内部legend百分比保留整数方法

业务场景&#xff1a;1、tooltip的背景需要渐变色&#xff0c;写 html 标签&#xff0c; 2、饼图内部的百分比需要保留整数 &#xff0c;使用formatter&#xff0c; export function genChartPieOption(pieData) {const res {replaceMerge: [series,], // 解决刷新之后y轴丢失…

P1883 函数

题目链接 P1883 函数 思路 举例 题目中的 F ( x ) F(x) F(x) 看起来很复杂&#xff0c;但由于每个 f ( x ) f(x) f(x) 的二次项系数 a a a 都不是负数&#xff0c;故 F ( x ) F(x) F(x) 是一个单谷函数。直接说出结论可能有些令人难以接受&#xff0c;不妨举出两个例子…

动物分类识别教程+分类释义+界面展示

1.项目简介 动物分类教程分类释义界面展示 动物分类是生物学中的一个基础知识&#xff0c;它是对动物进行分类、命名和描述的科学方法。本教程将向您介绍动物分类的基本原则和方法&#xff0c;并提供一些常见的动物分类释义。 动物分类的基本原则 动物分类根据动物的形态、…

Linux系统中的地址映射

一. 简介 在前面的裸机开发实验 LED灯实验中 &#xff0c;其实就是操作 IMX6ULL芯片的寄存器。 Linux 驱动开发也可以操作寄存器&#xff0c;但是&#xff0c;Linux不能直接对寄存器物理地址进行读写操作&#xff0c;例如&#xff0c;寄存器 A的物理地址为 0X01010101。 裸机…

2023亚马逊云科技re:Invent用Amazon Q打造你的知识库

随着ChatGPT的问世&#xff0c;我们迎来了许多创新和变革的机会。一年一度的亚马逊云科技大会re:Invent也带来了许多前言的技术&#xff0c;其中亚马逊云科技CEO Adam Selipsky在2023亚马逊云科技re:Invent大会中重磅推出Amazon Q&#xff0c;这预示着生成式AI的又一个里程碑。…

09.list 容器

9、list 容器 功能&#xff1a; 将数据进行链式存储 链表&#xff08;list&#xff09;是一种物理存储单元上非连续的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接实现的 链表的组成&#xff1a; 链表由一系列结点组成 结点的组成&#xff1a; 一个是存…

Canal使用详解

Canal介绍 Canal是阿里巴巴开发的MySQL binlog增量订阅&消费组件&#xff0c;Canal是基于MySQL二进制日志的高性能数据同步系统。在阿里巴巴集团中被广泛使用&#xff0c;以提供可靠的低延迟增量数据管道。Canal Server能够解析MySQL Binlog并订阅数据更改&#xff0c;而C…

【redis笔记】

Redis简介 安装步骤 Redis存储的是key-value结构的数据&#xff0c;其中key是字符串类型&#xff0c;value有5种常用的数据类型&#xff1a; 字符串string ​ 哈希hash 适合存储对象 列表list 按照插入顺序排序&#xff0c;可以有重复元素 集合set 无序集合&#xff0c;没…

[Android]CheckBox复选框

在Android开发中&#xff0c;复选框&#xff08;CheckBox&#xff09;是一种常用的控件&#xff0c;用于让用户在多个选项中进行选择。它通常用于表单中&#xff0c;让用户选择多个选项或者进行多项操作。在本篇博客中&#xff0c;我们将介绍如何在Android应用中使用CheckBox控…

Android MVI架构之UI状态的持有与保存

Android MVI架构之UI状态的持有与保存 我们将介绍状态持有者和其他与 UI 层相关的主题&#xff0c;例如在 Android 上提升状态和保存 UI 状态的位置。 状态持有者 状态持有者通过处理逻辑和/或公开 UI 状态来简化 UI。在本节中&#xff0c;我们将看到如何实现状态持有者以及…

Linux安装idea

目录 1.下载网址 2.解压安装 2.1新建idea安装路径 2.2解压压缩包到指定目录 2.3运行idea 3.下载Java环境 3.1命令行下载方式&#xff08;建议自行下载较新版本一步到位&#xff09; 3.2查看java版本 3.3版本不满意卸载当前jdk 3.4从官网下载较新的deb包进行下载 3.5解…