BMP图片格式解析及在嵌入式设备上的显示

1. BMP图片格式简介

BMP(Bitmap)是Windows操作系统中的标准图像文件格式,采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩。由于其格式简单,在嵌入式系统中被广泛使用。

2. BMP文件结构

BMP文件由四个部分组成:文件信息头、位图信息头、颜色表和像素数据。对于24位BMP图片,颜色表通常不存在。
在这里插入图片描述

2.1 文件信息头(BITMAPFILEHEADER)

文件信息头包含BMP文件的类型、大小和位图数据的起始位置等信息。

字段名称大小(字节)描述
bfType2文件类型,对于BMP文件,这两个字节的值是0x4D42,即字符’BM’
bfSize4文件大小,以字节为单位,表示整个BMP文件的大小
bfReserved12保留字节,必须设置为0
bfReserved22保留字节,必须设置为0
bfOffBits4从文件头到位图数据的偏移量,以字节为单位

2.2 位图信息头(BITMAPINFOHEADER)

位图信息头包含BMP图像的宽度、高度、压缩格式和颜色等信息。

字段名称大小(字节)描述
biSize4信息头的大小,以字节为单位。默认这个值是40
biWidth4图像的宽度,以像素为单位
biHeight4图像的高度,以像素为单位。如果此值为正,表示图像是倒立的(即数据从图像的底部开始)
biPlanes2图像的位面数,必须为1
biBitCount2每个像素的位数。对于24位BMP图像,此值为24
biCompression4图像的压缩类型。对于不压缩的图像,此值为0
biSizeImage4图像的大小,以字节为单位。对于不压缩的图像,这可以设置为0
biXPelsPerMeter4水平分辨率,每米的像素数。通常设置为0
biYPelsPerMeter4垂直分辨率,每米的像素数。通常设置为0
biClrUsed4使用的颜色数。如果为0,则表示使用所有可能的颜色。对于24位BMP图像,此值通常设置为0
biClrImportant4重要颜色的数量。如果为0,则表示所有颜色都是重要的。对于24位BMP图像,此值通常设置为0

2.3 像素数据

  • 每个像素用3字节表示(B、G、R顺序,即蓝、绿、红)
  • 图像数据按行存储,从下到上(第一行数据是实际图像最后一行颜色)
  • 每行像素数据必须填充到4字节的倍数(行对齐)
    • 例如:宽度为3像素(9字节)的行会填充到12字节

3. 24位BMP图片显示原理

在嵌入式系统中显示BMP图片的基本原理:

  1. 读取文件头信息:获取图片的宽度、高度和像素数据偏移量
  2. 处理像素数据:按照BGR顺序读取每个像素的颜色值
  3. 处理行对齐:跳过每行末尾的填充字节
  4. 写入显示设备:将像素数据写入帧缓冲区(如/dev/fb0

4. 示例代码解析

4.1 读取BMP文件信息的代码

#include <stdio.h>
#include <errno.h>
#include <string.h>

// 设置最大对齐数1,读取文件时,每个成员刚好保存对应位置的数据
#pragma pack(1) 
typedef struct
{
    unsigned short bfType;
    unsigned int bfSize;
    unsigned short bfReserverd1;
    unsigned short bfReserverd2;
    unsigned int bfOffBits;
} bmpfile_head_t; // bmp文件信息头

typedef struct
{
    unsigned int biSize;
    unsigned int biWidth;
    int biHeight;
    unsigned short biPlanes;
    unsigned short biBitCount;
    unsigned int biCompression;
    unsigned int biSizeImage;
    unsigned int biXPelsPerMeter;
    unsigned int biYPelsPerMeter;
    unsigned int biClrUsed;
    unsigned int biClrmportant;
} bmpbit_head_t; // bmp位图信息头
#pragma pack()   // 取消设置的对齐数,还原为默认

int read_bmp_info(char *bmppath)
{
    bmpfile_head_t filehead;
    bmpbit_head_t bithead;
    FILE *bmpfp = fopen(bmppath, "r");
    if (bmpfp == NULL)
    {
        printf("open %s: %s\r\n", bmppath, strerror(errno));
        return -1;
    }
    fread(&filehead, sizeof(filehead), 1, bmpfp); // 读取bmp文件信息头
    fread(&bithead, sizeof(bithead), 1, bmpfp);   // 读取bmp位图信息头
    
    // 打印bmp文件信息头
    printf("bfType:%#x\nbfSize:%d\nbfOffBits:%d\n", 
           filehead.bfType, filehead.bfSize, filehead.bfOffBits);

    // 打印bmp位图信息头
    printf("biWidth:%d\nbiHeight:%d\nbiBitCount:%d\nbiSizeImage:%d\n",
           bithead.biWidth, bithead.biHeight, bithead.biBitCount, bithead.biSizeImage);

    if (bithead.biBitCount / 8 != 3)
    {
        printf("The picture is not a 24-bit bmp!!!\r\n");
        fclose(bmpfp);
        return -1;
    }

    fclose(bmpfp);
    return 0;
}

4.2 在开发板上显示24位BMP图片的代码

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

// 结构体定义同上...

/** 
 * bmppath: 图片路径
 * lcdp: 屏幕文件内存映射后的内存
 */
int show_24bmp(char *bmppath, unsigned int *lcdp)
{
    bmpfile_head_t filehead;
    bmpbit_head_t bithead;
    FILE *bmpfp = fopen(bmppath, "r");
    if (bmpfp == NULL)
    {
        printf("open %s: %s\r\n", bmppath, strerror(errno));
        return -1;
    }
    fread(&filehead, sizeof(filehead), 1, bmpfp); // 读取bmp文件信息头
    fread(&bithead, sizeof(bithead), 1, bmpfp);   // 读取bmp位图信息头
    
    if (bithead.biBitCount != 24)
    {
        printf("The picture is not a 24-bit bmp!!!\r\n");
        fclose(bmpfp);
        return -1;
    }

    // 读取像素数据,通过内存映射方法写入到文件中
    unsigned char *readbuf = calloc(1, bithead.biWidth * 3);
    int pad = (4 - bithead.biWidth * 3 % 4) % 4; // 计算每行填充字节数

    // 读一行,写一行(从下往上读取,从上往下显示)
    for (int y = bithead.biHeight - 1; y >= 0; y--)
    {
        fread(readbuf, bithead.biWidth * 3, 1, bmpfp);
        fseek(bmpfp, pad, SEEK_CUR); // 跳过填充字节
        for (int x = 0; x < bithead.biWidth; x++)
        {
            // 将BGR转换为RGB并写入显存
            *(lcdp + y * 1024 + x) = readbuf[0 + x * 3] |        // 蓝色分量
                                     readbuf[1 + x * 3] << 8 |   // 绿色分量
                                     readbuf[2 + x * 3] << 16;   // 红色分量
        }
    }

    free(readbuf);
    fclose(bmpfp);
    return 0;
}

int main(void)
{
    int lcdfd = open("/dev/fb0", O_RDWR);
    if (lcdfd < 0)
    {
        perror("open /dev/fb0 fail...");
        return -1;
    }
    
    // 内存映射帧缓冲区
    unsigned int *lcdp = mmap(NULL, 1024 * 600 * 4, PROT_READ | PROT_WRITE, 
                             MAP_SHARED, lcdfd, 0);
    if (lcdp == MAP_FAILED)
    {
        perror("mmap /dev/fb0 fail...");
        close(lcdfd);
        return 0;
    }

    show_24bmp("./01.bmp", lcdp); // 显示图片

    close(lcdfd);
    munmap(lcdp, 1024 * 600 * 4);

    return 0;
}

5. 关键技术点

5.1 结构体对齐

使用#pragma pack(1)确保结构体成员按1字节对齐,这样可以准确读取BMP文件头中的数据。

5.2 行对齐处理

BMP文件要求每行数据必须是4字节的倍数,需要通过计算填充字节数并跳过这些字节。

5.3 图像方向处理

由于BMP文件从下往上存储像素数据,而显示设备从上往下显示,需要在读取时进行反向处理。

5.4 颜色格式转换

BMP文件使用BGR格式,而大多数显示设备使用RGB格式,需要进行颜色分量的重新排列。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值