经过一天的实践,终于弄明白的BMP图片的格式,并完成了正确读取图片信息,并生成新的BMP图片。
写程序前,首先要先了解一下几个知识点:
1. BMP图片的文件头、信息头,一共是54个字节,具体结构和意义在程序中有详细注释;
2. BMP按照每个像素的颜色占用的bit数分:1位(2色)、4位(16色)、8位(256色)、24位(真彩色)、32位(带透明度信息),一共五种,其中除24位和32位外,都需要使用调色板;
3. BMP中的图像数据按照每行32位对齐,不足32位用0补齐,故每行实际的长度是(像素宽度*像素占用bit数+31)/32个bit位;
附上详细程序,实现了读取一副bmp图片信息,重新生成一副bmp图像的功能,同时分别取出了每个像素的RGB三个分量值,分别存入三个文件。
#include
#include
typedef unsigned short int WORD;
typedef unsigned int DWORD;
typedef unsigned char BYTE;
typedef struct tagBITMAPFILEHEADER
{
WORD bfType; // 位图文件类型,必须为‘BM’(0-1字节)
DWORD bfSize; // 位图文件大小,以字节为单位(2-5字节)
WORD bfReserved1; // 位图文件保留字,必须为0(6-7字节)
WORD bfReserved2; // 位图文件保留字,必须为0(8-9字节)
DWORD bfOffBits; // 位图数据的起始位置(10-13字节)
}BITMAPFILEHEAEDER;
typedef struct tagBITMAPINFOHEADER
{
DWORD biSize; // 位图信息头占用的字节数(14-17字节)
DWORD biWidth; // 位图的宽度,以像素为单位(18-21字节)
DWORD biHeight; // 位图的高度,以像素为单位(22-25字节)
WORD biPlanes; // 目标设备的级别,必须为1(26-27字节)
WORD biBitCount; // 1(双色),4(16色),8(256色),24(全彩色)(28-29字节)
DWORD biCompression; // 位图压缩类型:0(不压缩),1(BI_RLES8),2(BI_RLE4)(30-33字节)
DWORD biSizeImage; // 位图的大小,以字节为单位(34-37字节)
DWORD biXPelsPerMeter;// 位图水平分辨率,每米像素数(38-41字节)
DWORD biYPelsPerMeter;// 位图垂直分辨率,每米像素数(42-45字节)
DWORD biClrUsed; // 位图实际使用的颜色数,0表示全部使用(46-49字节)
DWORD biClrImportant; // 位图显示过程中的重要颜色(50-53字节)
}BITMAPINFOHEADER;
typedef struct taRGBQUAD
{
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved; // 保留字,必须为0
}RGBQUAD;
int main(int argc, char** argv)
{
FILE *result_r; // 输出三个通道文件,读取每个像素点的颜色分量
FILE *result_g;
FILE *result_b;
FILE *bmp; // 原始bmp图像
FILE *copy; // 完全复制一副bmp图像
long real_w;
long offset = 0;
BITMAPFILEHEAEDER header;
BITMAPINFOHEADER info;
// 文化读写方式都使用二进制,否则会出错
if(!(bmp = fopen("oral.bmp", "rb")))
{
printf("the file bmp isn't exist.\n");
exit(0);
}
if(!(copy = fopen("copy.bmp", "wb")))
{
printf("create new file failed.\n");
exit(0);
}
// 复制文件头和信息头
fread(&header, 14, 1, bmp);
fwrite(&header, 14, 1, copy);
fread(&info, 40, 1, bmp);
fwrite(&info, 40, 1, copy);
// 输出原始bmp文件的宽高,压缩情况,位图类型
printf("width: %d, height: %d\n", info.biWidth, info.biHeight);
printf("size of pixel: %d\n", info.biBitCount);
printf("type of compression: %d\n", info.biCompression);
printf("count of colors: %d\n", info.biClrUsed);
result_r = fopen("red", "wb");
result_g = fopen("green", "wb");
result_b = fopen("blue", "wb");
// bmp每行要求是32位对齐,不足32位补齐,real_w保存的是每行实际的字节数
// offset是每行为了对齐32位,而填充的字节数
real_w = (info.biWidth * info.biBitCount + 31) / 32 * 4;
offset = real_w - (info.biWidth * info.biBitCount + 7) / 8;
char *w = (char*)malloc(offset);
printf("offset: %d\n", offset);
if(info.biBitCount == 24 || info.biBitCount == 32) // 真彩色
{
int i = 0, j;
for(; i < info.biHeight; i++)
{
j = 0;
for(; j < info.biWidth; j++)
{
RGBQUAD rgb;
char string[16];
fread(&rgb.rgbBlue, 1, 1, bmp);
fwrite(&rgb.rgbBlue, 1, 1, copy);
fread(&rgb.rgbGreen, 1, 1, bmp);
fwrite(&rgb.rgbGreen, 1, 1, copy);
fread(&rgb.rgbRed, 1, 1, bmp);
fwrite(&rgb.rgbRed, 1, 1, copy);
if(info.biBitCount == 32)
{
fread(&rgb.rgbReserved, 1, 1, bmp);
fwrite(&rgb.rgbReserved, 1, 1, copy);
}
sprintf(string, "%d ", rgb.rgbRed);
fputs(string, result_r);
sprintf(string, "%d ", rgb.rgbGreen);
fputs(string, result_g);
sprintf(string, "%d ", rgb.rgbBlue);
fputs(string, result_b);
}
if(offset && j != info.biWidth-1)
{
//fseek(bmp, offset, SEEK_CUR);
fread(w, offset, 1, bmp);
fwrite(w, offset, 1, copy);
}
fputs("\n", result_r);
fputs("\n", result_g);
fputs("\n", result_b);
}
free(w);
}
else // 索引颜色
{
int i = 0;
int index = 0;
int biClrUsed;
RGBQUAD *clTable; // 存储颜色表
biClrUsed = 1 << info.biBitCount;// 就是2^info.biBitCount
clTable = (RGBQUAD*)malloc(biClrUsed*sizeof(RGBQUAD));
for(; i < biClrUsed; i++) // 获取颜色表///
{
fread(&clTable[i], 4, 1, bmp);
fwrite(&clTable[i], 4, 1, copy);
clTable[i].rgbReserved = 0;
}
i = 0;
for(; i < info.biHeight; i++)
{
int j = 0;
WORD tmp; // 每次读一个字节
for(; j < info.biWidth; j++)
{
RGBQUAD rgb;
char string[16];
if(info.biBitCount == 8) // 8位,一个一个地取索引
{
fread(&tmp, 1, 1, bmp);
fwrite(&tmp, 1, 1, copy);
index = tmp;
}
else if(info.biBitCount == 4) // 4位,每两次循环取一次索引
{
if(j%2) index = tmp&0xF; // 奇数次取低四位
else
{
fread(&tmp, 1, 1, bmp);
fwrite(&tmp, 1, 1, copy);
index = tmp>>4; // 偶数次取高四位
}
}
else if(info.biBitCount == 1) // 1位,每8次循环取一次索引
{
int joffset = j % 8;
if(!joffset)
{
fread(&tmp, 1, 1, bmp);
fwrite(&tmp, 1, 1, copy);
}
index = (tmp>>(7-joffset))&0x1; // 取出一个字节,每次右移一位,按位与取最后一位值
}
rgb = clTable[index];
sprintf(string, "%d ", rgb.rgbRed);
fputs(string, result_r);
sprintf(string, "%d ", rgb.rgbGreen);
fputs(string, result_g);
sprintf(string, "%d ", rgb.rgbBlue);
fputs(string, result_b);
}
if(offset)
{
int i = 0;
fseek(bmp, offset, SEEK_CUR);
for(; i < offset; i++)
{
WORD zero = 0;
fwrite(&zero, 1, 1, copy);
}
}
fputs("\n", result_r);
fputs("\n", result_g);
fputs("\n", result_b);
}
free(clTable);
}
fclose(result_r);
fclose(result_g);
fclose(result_b);
fclose(bmp);
fclose(copy);
printf("解析完毕。\n");
return 0;
}