C语言读取BMP文件最关键的是要理解结构体对齐。一般情况下,C语言的结构体在内存中会按照4字节(32位)或者8字节(64位)对齐。BMP文件的数据头结构体按照字节排列,而且不能对齐,所以需要用预编译宏设置不能对齐。
另外注意Linux下一般用UTF-8编码,但是Windows下用GB2312,所以如果使用中文注释会造成乱码,在Windows下有奇怪的错误,因此跨平台尽量避免中文注释。
在Windows中,控制对齐的方法是:
#pragma pack(push,1)
#pragma pack(pop)
而在Linux的GCC中,控制对齐的方法是
typedef struct RGB_tag
{
char B;
char G;
char R;
} __attribute__((packed)) RGB_t;
所以在结构体定义方面需要分开写
bmptool.h
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef int int32_t;
#ifdef __GNUC__
typedef struct bmp_file_header_tag
{
uint8_t type[2];
uint32_t file_size;
uint16_t resv1;
uint16_t resv2;
uint32_t data_offset;
} __attribute__((packed)) bmp_file_header_t;
typedef struct bmp_info_tag
{
uint32_t info_size; // 40
uint32_t width;
uint32_t height;
uint16_t plane; // 1
uint16_t bit_count; // 24, no palette
uint32_t compression; // 0
uint32_t data_size;
uint32_t x_res; // 0
uint32_t y_res; // 0
uint32_t clr_used; // 0
uint32_t clr_important; // 0
} __attribute__((packed)) bmp_info_t;
typedef struct RGB_tag
{
char B;
char G;
char R;
} __attribute__((packed)) RGB_t;
typedef struct RGBQUAD_tag
{
char rgbBlue;
char rgbGreen;
char rgbRed;
char rgbReserved;
} RGBQUAD;
#endif
#ifdef _MSC_VER
#pragma pack(push,1)
typedef struct bmp_file_header_tag
{
uint8_t type[2]; //0,1
uint32_t file_size;//2,3,4,5
uint16_t resv1; //6,7
uint16_t resv2; //8,9
uint32_t data_offset; //10,11,12,13
} bmp_file_header_t;
typedef struct bmp_info_tag
{
uint32_t info_size; // 40
uint32_t width;
uint32_t height;
uint16_t plane; // 1
uint16_t bit_count; // 24, no palette
uint32_t compression; // 0
uint32_t data_size;
uint32_t x_res; // 0
uint32_t y_res; // 0
uint32_t clr_used; // 0
uint32_t clr_important; // 0
} bmp_info_t;
typedef struct RGB_tag
{
char B;
char G;
char R;
} RGB_t;
typedef struct RGBQUAD_tag
{
char rgbBlue;
char rgbGreen;
char rgbRed;
char rgbReserved;
} RGBQUAD;
#pragma pack(pop)
#endif
bmptool.c
int ReadBmp(char* bmpfile, int* width, int* height, int* bit, char* pBmpBuf)
{
bmp_info_t head;
int bmpWidth;
int bmpHeight;
int biBitCount;
int lineByte;
int i = 0;
FILE *fp=fopen(bmpfile,"rb");
if(fp==0)
return 0;
//printf("size is %d\n", sizeof(bmp_file_header_t));
fseek(fp, sizeof(bmp_file_header_t),0);
fread(&head, sizeof(bmp_info_t), 1,fp);
bmpWidth = head.width;
bmpHeight = head.height;
biBitCount = head.bit_count;
lineByte=(bmpWidth * biBitCount/8+3)/4*4;
if(biBitCount==8)
{
RGBQUAD pColorTable[256] = {0};
fread(pColorTable,sizeof(RGBQUAD),256,fp);
}
for(i = 0; i < bmpHeight; i++)
fread(pBmpBuf+i*bmpWidth, 1, lineByte, fp);
fclose(fp);
*width = bmpWidth;
*height = bmpHeight;
*bit = biBitCount;
return 1;
}
void CreateBmp(char* bytes, int width, int height, int bit, char* filename)
{
int i, j;
uint8_t pad[4] = { 0 };
int count = bit / 8;
int width2 = width * count;
int width_new = (width*count+3)/4*4;
int image_size = width_new * height;
int header_size = sizeof(bmp_file_header_t) + sizeof(bmp_info_t);
FILE* fp = NULL;
bmp_file_header_t header = { 'B','M', image_size + header_size, 0, 0, header_size};
bmp_info_t info = {sizeof(bmp_info_t), width, height, 1, bit, 0, image_size, 0,0, 0,0};
RGBQUAD pColorTable[256] = {0};
if (bit == 8)
{
header.file_size += 1024;
header.data_offset += 1024;
}
fp = fopen(filename, "wb");
fwrite(&header, sizeof(bmp_file_header_t), 1, fp);
fwrite(&info, sizeof(bmp_info_t), 1, fp);
if (bit == 8)
{
for (i = 0; i < 256; i++)
memset(&pColorTable[i], i, sizeof(RGBQUAD));
fwrite(pColorTable, 256*sizeof(RGBQUAD), 1, fp);
}
for (i = 0; i < height; i++)
{
fwrite(bytes + i*width2, 1, width2, fp);
fwrite(&pad, 1, width_new-width2, fp);
}
fclose(fp);
}
void RGB2Gray(char* im, int width, int height)
{
int i,j;
int size = width * height;
for (i = 0; i < size; i++)
im[i] = (im[3*i] + im[3*i+1] + im[3*i+2])/3;
}