BMP文件旋转

//一、要求将bmp文件顺时针旋转90°,要求熟悉BMP的文件格式及熟悉文件操作
//解题思路如下:
//1.BMP文件的组成此处不赘述,关键针对24位真彩图
//文件头组成
class Header				//文件头
{
public:
	WORD bftype;	//位图文件的类型,必须为“BM”
	DWORD bfsize;	//位图文件大小,以字节为单位,整个文件,包括位图与文件头和信息头
	WORD bfReserved1;	//位图文件保留字,必须为0
	WORD bfReserved2;	//位图文件保留字,必须为0
	DWORD bfOffBits;	//位图数据的起始位置,以相对于位图文件头的偏移量表示,以字节为单位
};

/其中WORD等为自定义数据类型以配合文档说
typedef unsigned char BYTE;	//BYTE为每个像素的RGB类,数值在0~255
typedef unsigned int DWORD;	//四个字节
typedef unsigned short WORD;	//两个字节
typedef long LONG;		//四个字节


信息头组成
class Info					//信息头
{
public:
	DWORD biSize;		//本类所用的字节数
	LONG biWidth;		//位图的宽度,以像素为单位
	LONG biHeight;		//位图的高度,以像素为单位
	WORD biPlanes;		//目标设备的平面数//不清,必须为1//什么
	WORD biBitCount;	//每个像素所需的位数,必须是1(双色),4(16色),8(256色)或24(真彩色)之一
	DWORD biCompression;	//位图压缩类型,必须是 0(不压缩),1(BI_RLE8压缩类型)或 2(BI_RLE4压缩类型)之一
	DWORD biSizeImage;		//位图的大小,以字节为单位
	LONG biXPelsPerMeter;	//位图水平分辨率,每米像素数
	LONG biYPelsPerMeter;	//位图垂直分辨率,每米像素数
	DWORD biClrUsed;		//位图实际使用的颜色表中的颜色数
	DWORD biClrImportant;	//位图显示过程中重要的颜色数
};

这说明bmp高H,宽W,每个坐标下(x,y)有一个像素,其中像素有3个字节,分别为RGB

读写文件
int main(int argc,char* argv[])
{
	char* src_name;
	char* dest_name;
	if(argc == 1)
	{
		cout<<"use \"src.bmp\" as default input file name,use \"dest.bmp\" as default output file name\n";
		src_name = new char[10];
		dest_name = new char[10];
		strcpy(src_name,"src.bmp");
		strcpy(dest_name,"dest.bmp");
	}
	else
	{
		src_name = argv[1];
		dest_name = argv[2];
	}
	Header fileHeader;
	Info infoHeader;
	ifstream infile(src_name,ios::in|ios::binary);
	if(!infile)
	{
		cout<<"读取文件失败"<<endl;
		return 0;
	}
	ofstream outfile(dest_name,ios::out|ios::binary);
	infile.read((char*) &fileHeader,sizeof(Header));
	infile.read((char*) &infoHeader,sizeof(Info));
	if(infoHeader.biBitCount == 24)
		Trans<RGB24>(infile,outfile,fileHeader,infoHeader);
	else
		Trans<RGB36>(infile,outfile,fileHeader,infoHeader);
	return 0;
}


//首先将src.bmp的文件头和信息头读入到内存的fileHeader 和infoHeader位置
//然后再进行旋转操作函数
//旋转函数的编写
//首先要改变目标函数的文件头数据和信息头数据,这里主要是文件头的bfSize即位图文件的大小和信息头的biWidth \biHeight\ biSizeImage
//其次要进行旋转操作,即改变位图旋转90°后的数据存储
//所以要先读入位图数据
//考虑到
//Windows 规定图像文件中一个扫描行所占的字节数必须是 4 的倍数(即以字为单位),不足的以 0 填充,图像文件中一个扫描行所占的字节数计算方法: 
//DataSizePerLine= (biWidth* biBitCount+31)/8;// 一个扫描行所占的字节数 
//即对于原始文件,其每行的后面是有补零的,所以读取时应跳过这部分
//PS:为什么每行读取字节公式是这样计算的呢?这是因为Windows 规定图像文件中一个扫描行所占的字节数必须是 4 的倍数(即以字为单位),即每个扫描行/读取的位数必须为32的倍数,向上取整,不足补零,所以要加上32-1,则DataSizePerLine= (biWidth* biBitCount+31)/32*4;
//则要计算每个扫描行的补零的字节数
//一种写法:
int getDiff(Info & info)
{
	int DataSizePerline = (info.biWidth * info.biBitCount+31) / 8;	// 一个扫描行实际所占的字节数
	DataSizePerline -= DataSizePerline % 4;				//为什么要这行捏,DataSizePerline不是4的倍数吗
	return DataSizePerline - info.biWidth * info.biBitCount /8;		//
}

//另一种写法
int getDiff(Info & info)
{
	return 4 - ((info.biWidth * info.biBitCount)/8)%4;		//但是这种写法好像不能解决图片2
}


//接下来便是旋转函数
template < class T >
bool Trans(ifstream & infile,ofstream & outfile,Header & header,Info & info)
{
	Header new_header = header;
	Info new_info = info;
	new_info.biWidth = info.biHeight;
	new_info.biHeight = info.biWidth;


	int diff = getDiff(info);
	T* pic = new T[info.biHeight * info.biWidth];


	for(int i=0;i<info.biHeight;i++)						//将src.bmp里的位图数据读入到pic数组中
	{
		infile.read((char*)(pic+info.biWidth*i),sizeof(T)*info.biWidth);	//读取每行biWidth个像素所占的字节数,即去补零的真实数据
		infile.seekg(diff,ios::cur);										//diff为正,跳过每行补零的字节,向后移动diff个字节
	}
	diff = getDiff(new_info);
	char* null = new char[diff+1];							//
	memset(null,0,diff+1);


	new_info.biSizeImage = (new_info.biWidth*sizeof(T) + diff) * new_info.biHeight*sizeof(T);				//新的位图大小
	new_header.bfsize = new_info.biSizeImage + sizeof(new_info) + sizeof(new_header);	//新的位图文件大小


	T* new_pic = new T[new_info.biHeight * new_info.biWidth];


	for(int i=0;i<new_info.biHeight;i++)					//将src.bmp里读取到pic数组的位图数据旋转得到dest.bmp的里的位图数据,存入到new_pic数组中
	{
		for(int j=0;j<new_info.biWidth;j++)
		{
			*(new_pic+i*new_info.biWidth+j)= *(pic + j*info.biWidth + new_info.biHeight-1-i);	//?????
		}
	}


	outfile.write((char*)&new_header,sizeof(Header));	//将文件头写入到dest.bmp
	outfile.write((char*)&new_info,sizeof(Info));		//将信息头写入到dest.bmp


	for(int i=0;i<new_info.biHeight;i++)					//将位图数据写入到dest.bmp
	{
		outfile.write((char*)(new_pic + new_info.biWidth*i),new_info.biWidth*sizeof(T));	//将new_pic里面的数据写到dest.bmp文件
		outfile.write((char*)null,diff);					//每行补零,写到dest.bmp文件
	}
	return true;
}
//总的代码
/*
 * rotatebmp.cpp
 *
 *  Created on: 2014-4-17
 */


#include<iostream>
#include<fstream>
#include<cstring>
#include<string>


#pragma pack(1)


using namespace std;


typedef unsigned char BYTE;
typedef unsigned int DWORD;
typedef unsigned short WORD;
typedef long LONG;


class Header				//文件头
{
public:
	WORD bftype;	//位图文件的类型,必须为“BM”
	DWORD bfsize;	//位图文件大小,以字节为单位,整个文件,包括位图与文件头和信息头
	WORD bfReserved1;	//位图文件保留字,必须为0
	WORD bfReserved2;	//位图文件保留字,必须为0
	DWORD bfOffBits;	//位图数据的起始位置,以相对于位图文件头的偏移量表示,以字节为单位
};
class Info					//信息头
{
public:
	DWORD biSize;		//本类所用的字节数
	LONG biWidth;		//位图的宽度,以像素为单位
	LONG biHeight;		//位图的高度,以像素为单位
	WORD biPlanes;		//目标设备的平面数//不清,必须为1//什么
	WORD biBitCount;	//每个像素所需的位数,必须是1(双色),4(16色),8(256色)或24(真彩色)之一
	DWORD biCompression;	//位图压缩类型,必须是 0(不压缩),1(BI_RLE8压缩类型)或 2(BI_RLE4压缩类型)之一
	DWORD biSizeImage;		//位图的大小,以字节为单位
	LONG biXPelsPerMeter;	//位图水平分辨率,每米像素数
	LONG biYPelsPerMeter;	//位图垂直分辨率,每米像素数
	DWORD biClrUsed;		//位图实际使用的颜色表中的颜色数
	DWORD biClrImportant;	//位图显示过程中重要的颜色数
};
class RGB24
{
public:
	RGB24()
	{
		GREEN = 0;
		RED = 0;
		BLUE = 0;
	}
	BYTE RED;	//红色的亮度(值范围在0~255)
	BYTE GREEN;	//绿色的亮度(值范围在0~255)
	BYTE BLUE;	//蓝色的亮度(值范围在0~255)
};
/*找到每行需要补0的字节数 */
int getDiff(Info & info)
{
	int DataSizePerline = (info.biWidth * info.biBitCount+31) / 8;	// 一个扫描行实际所占的字节数
	DataSizePerline -= DataSizePerline % 4;
	return DataSizePerline - info.biWidth * info.biBitCount / 8;		//返回
}
template < class T >
bool Trans(ifstream & infile,ofstream & outfile,Header & header,Info & info)
{
	Header new_header = header;
	Info new_info = info;
	new_info.biWidth = info.biHeight;
	new_info.biHeight = info.biWidth;


	int diff = getDiff(info);
	T* pic = new T[info.biHeight * info.biWidth];


	for(int i=0;i<info.biHeight;i++)						//将src.bmp里的位图数据读入到pic数组中
	{
		infile.read((char*)(pic+info.biWidth*i),sizeof(T)*info.biWidth);	//读取每行biWidth个像素所占的字节数,即去补零的真实数据
		infile.seekg(diff,ios::cur);										//diff为正,跳过每行补零的字节,向后移动diff个字节
	}
	diff = getDiff(new_info);
	char* null = new char[diff+1];							//
	memset(null,0,diff+1);


	new_info.biSizeImage = (new_info.biWidth*sizeof(T) + diff) * new_info.biHeight*sizeof(T);				//新的位图大小
	new_header.bfsize = new_info.biSizeImage + sizeof(new_info) + sizeof(new_header);	//新的位图文件大小


	T* new_pic = new T[new_info.biHeight * new_info.biWidth];


	for(int i=0;i<new_info.biHeight;i++)					//将src.bmp里读取到pic数组的位图数据旋转得到dest.bmp的里的位图数据,存入到new_pic数组中
	{
		for(int j=0;j<new_info.biWidth;j++)
		{
			*(new_pic+i*new_info.biWidth+j)= *(pic + j*info.biWidth + new_info.biHeight-1-i);	//?????
		}
	}


	outfile.write((char*)&new_header,sizeof(Header));	//将文件头写入到dest.bmp
	outfile.write((char*)&new_info,sizeof(Info));		//将信息头写入到dest.bmp


	for(int i=0;i<new_info.biHeight;i++)					//将位图数据写入到dest.bmp
	{
		outfile.write((char*)(new_pic + new_info.biWidth*i),new_info.biWidth*sizeof(T));	//将new_pic里面的数据写到dest.bmp文件
		outfile.write((char*)null,diff);					//每行补零,写到dest.bmp文件
	}
	return true;
}
int main(int argc,char* argv[])
{
	char* src_name;
	char* dest_name;
	if(argc == 1)
	{
		cout<<"use \"src.bmp\" as default input file name,use \"dest.bmp\" as default output file name\n";
		src_name = new char[10];
		dest_name = new char[10];
		strcpy(src_name,"src.bmp");
		strcpy(dest_name,"dest.bmp");
	}
	else
	{
		src_name = argv[1];
		dest_name = argv[2];
	}
	Header fileHeader;
	Info infoHeader;
	ifstream infile(src_name,ios::in|ios::binary);
	if(!infile)
	{
		cout<<"读取文件失败"<<endl;
		return 0;
	}
	ofstream outfile(dest_name,ios::out|ios::binary);
	infile.read((char*) &fileHeader,sizeof(Header));
	infile.read((char*) &infoHeader,sizeof(Info));
	if(infoHeader.biBitCount == 24)
		Trans<RGB24>(infile,outfile,fileHeader,infoHeader);
	return 0;
}






  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值