嵌入式Linux--读取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;
}

执行结果如下:
嵌入式Linux--读取bmp文件头信息_第1张图片
显示bfSize(文件大小)为28,这显然是不对的。根据WinHex查看图片可以看到,实际数据为0x36 0x20 0x1C 0x00,而在BMP文件中数据存储方式为小端模式(低地址存放低字节),因此实际bfSize为1843254(0x1C2036)个字节。
嵌入式Linux--读取bmp文件头信息_第2张图片

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

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;
}
嵌入式Linux--读取bmp文件头信息_第3张图片

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

  • 结构体变量的首地址是其最宽基本类型成员大小的整数倍;
  • 结构体成员首地址的偏移量(相对于结构体首地址) 是成员大小的整数倍;
  • 结构体的总大小为结构体最宽基本类型成员大小的整数倍;
  • 如有需要编译器会在成员之间加上填充字节;
    嵌入式Linux--读取bmp文件头信息_第4张图片

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

  • 在定义结构体之前使用伪指令#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;	

原链接本来默认就取消了字节对齐,但是一开始并不了解具体深意,于是就被我删掉了,看来还是得加上。
调试结果如下图:
嵌入式Linux--读取bmp文件头信息_第5张图片
到这里就显示正常了,1843254就是0x1C2036,还有一个问题是伪指令#pragma pack ()没效果,不知道是gcc的问题还是什么其他的问题。

你可能感兴趣的:(Linux嵌入式开发)