C语言读写BMP文件
今天用到C编写读写bmp文件的事情,主要针对24位bmp位图,关键要注意的问题是:
[像素的数据量并不一定完全等于图象的高度乘以宽度乘以每一像素的字节数,而是可能略大于这个值。原因是BMP文件采用了一种”对齐” 的机制,每一行像素数据的长度若不是4的倍数,则填充一些数据使它是4的倍数。这样一来,一个17*15的24位BMP大小就应该是834字节(每行17 个像素,有51字节,补充为52字节,乘以15得到像素数据总长度780,再加上文件开始的54字节,得到834字节)。分配内存时,一定要小心,不能直接使用”图象的高度乘以宽度乘以每一像素的字节数”来计算分配空间的长度,否则有可能导致分配的内存空间长度不足,造成越界访问,带来各种严重后果。]
typedef struct BMPSTRUCT {
void * data; //数据指针
int bmpWidth; //图片宽度
int bmpHeight; //图片高度
unsigned char bmpBiBitCount; //bmp类型 1 4 8 24 32
}BmpDataBox;
//宏定义 用于固定的偏移
#define DataOffset 10
#define SizeOffset 18
#define BiBitCount 2
#define COLORTABLE 54
// 读取bmp文件 (24位)
BmpDataBox* readBmp(char *BmpFileName)
{
FILE * pFile;
BmpDataBox * newBmpData = NULL;
int dataOffset;
pFile = fopen(BmpFileName,"r");
if(!pFile)
{
return newBmpData;
}
newBmpData = (BmpDataBox *)malloc(sizeof(BmpDataBox));
//printf("file %s open successs!\n",bmpPath);
//得到数据的偏移字节
fseek(pFile,DataOffset,SEEK_SET);
fread(&dataOffset,4,1,pFile);
//得到bmp的宽与高的像素数
fseek(pFile,SizeOffset,SEEK_SET);
fread(&newBmpData->bmpWidth,4,1,pFile);
fread(&newBmpData->bmpHeight,4,1,pFile);
//注意,有些图片存在取出为负的情况,我也没弄清楚
newBmpData->bmpWidth = abs(newBmpData->bmpWidth);
newBmpData->bmpHeight = abs(newBmpData->bmpHeight);
fseek(pFile,BiBitCount,SEEK_CUR);
fread(&newBmpData->bmpBiBitCount, 2, 1, pFile);
//printf("biBitCount %d\n",newBmpData->bmpBiBitCount);
//位图片数据分配空间
int BytesPerPixel = newBmpData->bmpBiBitCount / 8;
int LineLength, TotalLength;
LineLength = newBmpData->bmpWidth * BytesPerPixel; // 每行数据长度大致为图象宽度乘以
// 每像素的字节数
while( LineLength % 4 != 0 ) // 修正LineLength使其为4的倍数
++LineLength;
TotalLength = LineLength * newBmpData->bmpHeight; // 数据总长 = 每行长度 * 图象高度
newBmpData->data = (unsigned char *)malloc(TotalLength * sizeof(unsigned char));
fseek(pFile,dataOffset,SEEK_SET);
fread(newBmpData->data , sizeof(unsigned char), (size_t)(long)TotalLength, pFile);
fclose(pFile);
return newBmpData;
}
#define RGBA_TYPE 4
#define RGB_TYPE 3
// 写入bmp文件(24位)
bool SaveBmp(GLint w, GLint h, unsigned char *pdata, char *BmpFileName, bool IsRGBA )
{
#define BMP_Header_Length 54
unsigned char header[BMP_Header_Length] = {
0x42, 0x4d, 0, 0, 0, 0, 0, 0, 0, 0,
54, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, IsRGBA ? 32 : 24, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0
};
long file_size = (long)w * (long)h * (IsRGBA ? 4 : 3) + 54;
header[2] = (unsigned char)(file_size &0x000000ff);
header[3] = (file_size >> 8) & 0x000000ff;
header[4] = (file_size >> 16) & 0x000000ff;
header[5] = (file_size >> 24) & 0x000000ff;
long width = w;
header[18] = width & 0x000000ff;
header[19] = (width >> 8) &0x000000ff;
header[20] = (width >> 16) &0x000000ff;
header[21] = (width >> 24) &0x000000ff;
long height = h;
header[22] = height &0x000000ff;
header[23] = (height >> 8) &0x000000ff;
header[24] = (height >> 16) &0x000000ff;
header[25] = (height >> 24) &0x000000ff;
FILE *pWritingFile = NULL;
pWritingFile = fopen(BmpFileName, "wb");
if( pWritingFile == NULL )
return false;
fwrite(header, sizeof(unsigned char), 54, pWritingFile);
int BytesPerPixel = IsRGBA ? 4 : 3;
int LineLength, TotalLength;
LineLength = w * BytesPerPixel; // 每行数据长度大致为图象宽度乘以
// 每像素的字节数
while( LineLength % 4 != 0 ) // 修正LineLength使其为4的倍数
++LineLength;
TotalLength = LineLength * h; // 数据总长 = 每行长度 * 图象高度
//fwrite(pdata, PixelDataLength, 1, pWritingFile);
fwrite(pdata, sizeof(unsigned char), (size_t)(long)TotalLength, pWritingFile);
// 释放内存和关闭文件
fclose(pWritingFile);
return true;
}