解题报告:
做这道题要仔细阅读老师提供的文档,熟悉了24位位图的数据规模后再读入。
数据读入:对于图像数据分行读入,设置一个后移块,用于跳过每一行的补0位。
数据交换:a(width - i - 1,j) = b(j,i)
这里为了迎合题意,在main函数里加入两个参数int argc和char * argv[],这样可以直接从命令提示符cmd.exe中用如下图方式使用转换程序:
另外,代码里用到的template模板的思想来自thoar大牛的代码
大家可以去他的博客看到更多的代码:
http://blog.csdn.net/xranthoar
注意:老师提供的文档前面那篇文章可能有点问题,在图像描述信息块的最后一部分数据应该是002E-0035!
接下来贴代码:
因为手头没有32位位图,我不知道能不能旋转32位位图…
#include <iostream>
#include <fstream>
#pragma pack(1)
using namespace std;
const int CON_SIZE = 31; //公式中的大小。DataSizePerLine= (biWidth* biBitCount+31)/8;为一个扫描行所占的字节数
const int BYTE_SIZE = 8; //一个字节=8位。
typedef char BYTE; //1字节
typedef short DBYTE; //2字节
typedef int WORD; //4字节
typedef long LONG; //4字节
class FileHead //文件头信息块
{
public:
DBYTE bfType; //文件标识,为字母ASCII码“BM”
WORD bfSize; //文件大小
WORD bfReserved; //保留,每字节以“00”填写
WORD bfOffBits; //记录图像数据区的起始位置。各字节的信息依次含义为:文件头信息块大小,图像描述信息块的大小,图像颜色表的大小,保留(为01)
};
class InfoHead //图像描述信息块
{
public:
WORD biSize; //图像描述信息块的大小,常为28H
LONG biWidth; //图像宽度
LONG biHight; //图像高度
DBYTE biPlane; //图像的plane总数(恒为1)
DBYTE biBitCount; //记录像素的位数,很重要的数值,图像的颜色数由该值决定
WORD biCompression; //数据压缩方式(数值位0:不压缩;1:8位压缩;2:4位压缩)
WORD biSizeImage; //图像区数据的大小
LONG biXPelsPerMeter; //水平每米有多少像素,在设备无关位图(.DIB)中,每字节以00H填写
LONG biYPelsPerMeter; //垂直每米有多少像素,在设备无关位图(.DIB)中,每字节以00H填写
WORD biClrUsed; //此图像所用的颜色数
WORD biCleImportance; //颜色重要性,若为0,则表示颜色一样重要
//这里要注意!老师提供的文档前面那篇文章这里有错误,他说“002E-0031:此图像所用的颜色数,如值为0,表示所有颜色一样重要。”
//但实际上:这里应该是两个长为4字节的数据!(即biClrUsed和biCleImportance)
};
class RGBQuad_24 //24位位图图像数据
{
public:
RGBQuad_24() : rgbBlue(0), rgbGreen(0), rgbRed(0) { }; //补0用
BYTE rgbBlue; //蓝
BYTE rgbGreen; //绿
BYTE rgbRed; //红
};
class RGBQuad_32 //32位位图图像数据
{
public:
RGBQuad_32() : rgbBlue(0), rgbGreen(0), rgbRed(0), rgbAlpha(0) { }; //补0用
BYTE rgbBlue; //蓝
BYTE rgbGreen; //绿
BYTE rgbRed; //红
BYTE rgbAlpha; //透明度
};
FileHead Head;
InfoHead Info;
template <typename RGBQuad>
bool Deal(ifstream &fin, ofstream &fout)
{
RGBQuad *Quad;
int LineSize = (Info.biWidth * Info.biBitCount + CON_SIZE) / BYTE_SIZE;
LineSize -= LineSize % 4; //计算每一行的真实长度,在下面输入的时候用于过滤补0的数据
int DifPtr = LineSize - Info.biWidth * sizeof(RGBQuad); //用于跳过补0数据
int Scale = Info.biHight * Info.biWidth; //真实有用的数据规模
Quad = new RGBQuad [Scale]; //生成图片数据包
for(int i = 0; i != Info.biHight; ++i)
{
fin.read((char*) (Quad + i * Info.biWidth), Info.biWidth * sizeof(RGBQuad)); //读入有效数据
fin.seekg(DifPtr, ios::cur); //跳过补0位
}
FileHead DestHead = Head;
InfoHead DestInfo = Info;
DestInfo.biHight = Info.biWidth;
DestInfo.biWidth = Info.biHight;
int DestLineSize = (DestInfo.biWidth * DestInfo.biBitCount + CON_SIZE) / BYTE_SIZE;
DestLineSize -= DestLineSize % 4;
DifPtr = DestLineSize - DestInfo.biWidth * sizeof(RGBQuad);
Scale = DestInfo.biWidth * DestInfo.biHight;
DestHead.bfSize = DestInfo.biHight * DestLineSize + sizeof(FileHead) + sizeof(InfoHead);
DestInfo.biSizeImage = DestHead.bfSize - sizeof(FileHead) + sizeof(InfoHead);
RGBQuad *DestQuad = new RGBQuad [Scale];
for(int i = 0; i != DestInfo.biHight; ++i)
{
for(int j = 0; j != DestInfo.biWidth; ++j)
{
DestQuad[(DestInfo.biHight - i - 1) * DestInfo.biWidth + j] =
Quad[j * Info.biWidth + i];
}
} //交换数据
fout.write((char *) &DestHead, sizeof(FileHead));
fout.write((char *) &DestInfo, sizeof(InfoHead));
char *Temp = new char[DifPtr + 1];
memset(Temp, 0, DifPtr + 1);
for(int i = 0; i != DestInfo.biHight; ++i)
{
fout.write((char*) (DestQuad + DestInfo.biWidth * i), DestInfo.biWidth * sizeof(RGBQuad));
fout.write((char*) Temp, DifPtr);
}
cout << "处理完成!" << endl;
cout << "处理前文件大小 = " << Head.bfSize << endl;
cout << "处理后文件大小 = " << DestHead.bfSize << endl;
return true;
}
int main(int argc, char * argv[]) //传入控制台参数,在cmd里可以直接用rotatebmp src.bmp dest.bmp
{
if(argc == 1)
{
argv[1] = "src.bmp";
argv[2] = "dest.bmp"; //默认的图片文件名,缺省用
}
ifstream fin(argv[1], ios::in | ios::binary);
ofstream fout(argv[2], ios::out | ios::binary);
if(!fin)
{
cout << "找不到 " << argv[1] << " !" << endl;
return -1;
}
if(!fout)
{
cout << "无法打开 " << argv[2] << " !" << endl;
return -1;
}
fin.read((char *) &Head, sizeof(FileHead));
fin.read((char *) &Info, sizeof(InfoHead)); //2进制读入两个文件信息头
if(Info.biBitCount == 24)
{
Deal<RGBQuad_24>(fin, fout);
}
if(Info.biBitCount == 32)
{
Deal<RGBQuad_32>(fin, fout);
}
return 0;
}