读取bmp文件头信息

读取bmp文件头信息

文件头信息的格式定义可以参考这里,读取文件信息有两种方式,第一种方式是定义一个字符串,使用read函数将图像信息读取到字符串中,然后自己去提取想要的数据。这种方法需要我们提前知道图像文件头的格式定义,很麻烦且容易出错,适合初学阶段使用,熟悉了之后即可过渡使用第二种方式;第二种方式是使用bmp结构体,结构体可以自行定义,也可以去网上找,百度一大堆。定义结构体变量存储文件头信息这种方式的优点是格式清晰,代码可读性好。
我找的结构体如下,原文链接在这

//文件头结构体
typedef struct 
{ 
    unsigned char    bfType[2];     //文件类型
    unsigned long    bfSize;        //位图大小
    unsigned short bfReserved1;     //位0 
    unsigned short bfReserved2;     //位0
    unsigned long    bfOffBits;     //到数据偏移量
} BitMapFileHeader;

函数如下:

/*
 *函数功能:解析bmp图片
 *参数列表:path:要解析的图片的pathname
 *返回值 :错误时返回-1,解析正确返回0
 */
int bmp_analyze(unsigned char *path)
{
    int fd = -1;
    BitMapFileHeader fHeader;

    //打开bmp图片
    fd = open(path, O_RDONLY);
    if (fd < 0) {
        fprintf(stderr, "open %s error.\n", path);
        return -1;
    }

    //读取文件头信息
    read(fd, &fHeader, sizeof(fHeader));
    printf("bfSize:%ld\n", fHeader.bfSize);
    printf("bfOffBits:%ld\n", fHeader.bfOffBits);

    //关闭打开的文件
    close(fd);

    return 0;
}

执行结果如下:

显示bfSize(文件大小)为28,这显然是不对的。根据WinHex查看图片可以看到,实际数据为0x36 0x20 0x1C 0x00,而在BMP文件中数据存储方式为小端模式(低地址存放低字节),因此实际bfSize为1843254(0x1C2036)个字节。

下面开始查找问题。
将读取到的信息都打印出来,分析原始数据。

int bmp_analyze(unsigned char *path)
{
    int fd = -1, i;
    BitMapFileHeader fHeader;

    //打开bmp图片
    fd = open(path, O_RDONLY);
    if (fd < 0) {
        fprintf(stderr, "open %s error.\n", path);
        return -1;
    }

    //读取文件头信息
    read(fd, &fHeader, sizeof(fHeader));
    printf("bfSize:%ld\n", fHeader.bfSize);
    printf("bfOffBits:%ld\n", fHeader.bfOffBits);

    //将读取到的信息打印出来
    for (i = 0; i < sizeof(fHeader); i++) {
        printf("%x ", *((unsigned char *)&fHeader + i));
    }
    printf("\n");

    //关闭打开的文件
    close(fd);

    return 0;
}


这时就可以看到读取的结果,数据个数为16个!而结构体中的数据个数为14个!这就牵扯到字节对齐问题了,其实好早以前就知道这个概念,单纯的以为就是以牺牲空间为代价去提高处理器存取效率,没想到会对上层有影响!把结构体变量当成字符串去存储数据的方法也是我第一次使用,可见眼界还是低了点,不禁感叹C的博大精深!
这里扯一下字节对齐,以前也看了好多资料,总结一下就四点:

  • 结构体变量的首地址是其最宽基本类型成员大小的整数倍;
  • 结构体成员首地址的偏移量(相对于结构体首地址) 是成员大小的整数倍;
  • 结构体的总大小为结构体最宽基本类型成员大小的整数倍;
  • 如有需要编译器会在成员之间加上填充字节;

解决办法:取消字节对齐。
取消字节对齐的两种方法:

  • 在定义结构体之前使用伪指令#pragma pack ()
  • 在结构体名称之前加__attribute__((packed))

结构体定义于是被修改为了:

typedef struct 
{ 
    unsigned char    bfType[2];     //文件类型
    unsigned long    bfSize;        //位图大小
    unsigned short bfReserved1;     //位0 
    unsigned short bfReserved2;     //位0
    unsigned long    bfOffBits;     //到数据偏移量
} __attribute__((packed)) BitMapFileHeader; 

原链接本来默认就取消了字节对齐,但是一开始并不了解具体深意,于是就被我删掉了,遇到问题再去想办法解决,可能这就是学习的过程吧(ノ ̄▽ ̄)
调试结果如下图:

到这里就显示正常了,1843254就是0x1C2036,还有一个问题是伪指令#pragma pack ()没效果,不知道是gcc的问题还是芯片的问题。

猜你喜欢

转载自blog.csdn.net/Meteor_s/article/details/82425610