C语言实现BMP格式图片的读写

      经过一天的实践,终于弄明白的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;
}


你可能感兴趣的:(图形图像学习)