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

发布时间:2023年12月23日

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

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

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

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

文章来源:https://blog.csdn.net/qq_63831368/article/details/135157823
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。