//一、要求将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;
}