.bmp文件的数据结构较为简单, 且24, 32位.bmp文件的位图数据部分不进行压缩,实现图片的旋转是容易的。特别地,旋转90度的整数倍不涉及坐标取整等操作,更为简单。
以下提供笔者近期完成作业的自编代码段,实现对24位或32位.bmp文件顺时针旋转90度:
#include
#include
#include
#include
using namespace std;
#define TrueColor
#define OutWidth biHeight
#define OutHeight biWidth
//#define maxFileNameLength 50
#define maxnew 20*1024*1024
typedef unsigned char BYTE;//1B
typedef unsigned short WORD;//2B
typedef unsigned int DWORD;//4B
typedef long LONG;//4B
//=========================文件头========================
typedef struct tagBITMAPFILEHEADER
{
WORD bfType; //0000-0001 位图文件的类型,必须为“BM”(0x42 0x4D)
DWORD bfSize; //0002-0005 位图文件的大小,以字节为单位
WORD bfReserved1; //0006-0007位图文件保留字,必须为0
WORD bfReserved2; //0008-0009位图文件保留字,必须为0
DWORD bfOffBits;//000A-000D位图数据的起始位置,以相对于位图文件头的偏移量表示,以字节为单位
} BIT_MAP_FILE_HEADER;
//14个字节。
//======================位图信息头======================
typedef struct tagBITMAPINFOHEADER {
DWORD biSize; //000E-0011本结构所占用字节数
LONG biWidth; //0012-0015位图的宽度,以像素为单位
LONG biHeight; //0016-0019位图的高度,以像素为单位
WORD biPlanes; //001A-001B目标设备的平面数不清,必须为1
WORD biBitCount;//001C-001D每个像素所需的位数,必须是1(双色), 4(16色),8(256色)或24(真彩色)之一
DWORD biCompression; //001E-0021位图压缩类型,必须是 0(不压缩),1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一
DWORD biSizeImage; //0022-0025位图的大小,以字节为单位
LONG biXPelsPerMeter; //0026-0029位图水平分辨率,每米像素数
LONG biYPelsPerMeter; //002A-002D位图垂直分辨率,每米像素数
DWORD biClrUsed;//002E-0031位图实际使用的颜色表中的颜色数
DWORD biClrImportant;//0032-0035位图显示过程中重要的颜色数
} BIT_MAP_INFO_HEADER;
//40个字节。
//整个头部
struct WHOLEHEAD{
BIT_MAP_FILE_HEADER file_head;
BIT_MAP_INFO_HEADER file_info_head;
WHOLEHEAD() { ; }
WHOLEHEAD(const WHOLEHEAD& in_):
file_head(in_.file_head), file_info_head(in_.file_info_head)
{
file_info_head.biWidth = in_.file_info_head.biHeight;
file_info_head.biHeight = in_.file_info_head.biWidth;
file_info_head.biSizeImage =
fill_to_4(file_info_head.biWidth)*file_info_head.biHeight;
file_head.bfSize = file_info_head.biSizeImage + 0x36;
return;
}
}In;//54个字节
void Input_WholeHead(WHOLEHEAD& in_, ifstream& f);
void Show_WholeHead(WHOLEHEAD&in_);
//======================图像数据区==========================
//像素数据, 按字节存放, BGR
BYTE* PixelLine= NULL;
BYTE Blank[85];//用于拷贝32位文件头后的调色区
//文件名
char File_Name[maxFileNameLength] ;
//补齐
int fill_to_4(int nPixel) {
if(In.file_info_head.biBitCount == 24) {
int temp = ceil(3.0*nPixel / 4.0);
return 4 * temp;
}//24位
else if(In.file_info_head.biBitCount == 32) {
return 4 * nPixel;
}//32位
else return 0;
}
template<class T>
void UnitInput(T& dest, ifstream& in_) {
char* _temp_ =reinterpret_cast<char*>(&dest);
in_.read(_temp_, sizeof(T));
return;
}
template<class T>
void UnitOutput(T& src, ofstream& out_ ){
char* _temp_ = reinterpret_cast<char*> (&src);
out_.write(_temp_, sizeof(T));
return;
}
//打开文件, 读取,
//分配空间, 存入旋转之前的图像数据
void Read_Assign_Store(const char* fNAME_=NULL) {
//std::cout << "CALL[Read_Assign_Store]Input File Name:\n(The file should be within address \"AddSample\")\n";
//memset(File_Name, 0, maxFileNameLength);
//cin >> File_Name;
//cin.sync();//去掉输入流中的‘\n'
//ifstream fin(File_Name, ios::binary | ios::out);
ifstream fin(fNAME_, ios::binary | ios::out);
if(!fin) { cerr << "ERROR[Read_Assign_Store]Open Failed.\n"; return; }
std::cout << "CLAIM[Read_Assign_Store]Open successfully.\n";
fin.seekg(ios::beg);
Input_WholeHead(In, fin);
Show_WholeHead(In);
//**************************************************
for(int i = 0; i < (In.file_info_head.biSize-40); i++) { UnitInput(Blank[i], fin); }
//适用于V4和V5文件头
fin.seekg(ios::beg); fin.seekg(In.file_head.bfOffBits);
//**************************************************
//再输入位图数据至数组中
//原图
int OldWidth_p = In.file_info_head.biWidth;
int OldWidth_b = fill_to_4(OldWidth_p);
int OldHeight_p = In.file_info_head.biHeight;
//新图
int new_Height_p = OldWidth_p;
int new_Width_p = OldHeight_p;
int new_Width_b = fill_to_4(new_Width_p);
//
//一个像素占的字节数
const int ByteSinglePixel = In.file_info_head.biBitCount / 8;
PixelLine = new BYTE[maxnew];
if(!PixelLine) { cerr << "ERROR[Read_Assign_Store]new BYTE failure.\n"; return; }
memset(PixelLine, 0, maxnew);//sizeof(PixelLine==4)
register int count = 0; BYTE temp;
for(int i = 0; i < OldHeight_p; i++) {
for(int j = 0; j < OldWidth_b; j++) {
if(j < ByteSinglePixel * OldWidth_p) {
UnitInput(PixelLine[count], fin);
if(fin.fail()) { cerr << "ERROR[Read_Assign_Store]failed input to PixelLine.\n"; return; }
++count;
}//输入数组中
else {
UnitInput(temp, fin);//不使用
if(fin.fail()) { cerr << "ERROR[Read_Assign_Store]failed input to temp.\n"; return; }
}
}
}
//总数目不对
if(count != ByteSinglePixel * OldHeight_p*OldWidth_p) { cerr << "ERROR[Read_Assign_Store]Data missing.\n"; return; }
std::cout << "CLAIM[Read_Assign_Store]Input to PixelLine done.\n";//正常
//cout << "TEST[Read_Assign_Store]PixelLine:\n";
//for(int i = 0; i < 280; i++) { cout << hex <
fin.close();
return;
}
//头的输入
void Input_WholeHead(WHOLEHEAD& in_, ifstream& f) {
if(!f) { cerr << "ERROR[Input_WholeHead]Open Failed.\n"; return; }
std::cout << "CLAIM[Input_WholeHead]Start input.\n";
auto P = &(in_.file_head); auto Q = &(in_.file_info_head);
UnitInput(P->bfType, f); UnitInput(P->bfSize, f); UnitInput(P->bfReserved1, f);
UnitInput(P->bfReserved2, f); UnitInput(P->bfOffBits, f);
UnitInput(Q->biSize, f); UnitInput(Q->biWidth, f); UnitInput(Q->biHeight,f);
UnitInput(Q->biPlanes, f); UnitInput(Q->biBitCount, f); UnitInput(Q->biCompression, f);
UnitInput(Q->biSizeImage, f); UnitInput(Q->biXPelsPerMeter, f); UnitInput(Q->biYPelsPerMeter, f);
UnitInput(Q->biClrUsed, f); UnitInput(Q->biClrImportant, f);
return;
}
//头的检查
void Show_WholeHead(WHOLEHEAD&in_) {
auto P = &(in_.file_head); auto Q = &(in_.file_info_head);
std::cout << "-----------------------------文件头---------------------------\n";
std::cout << "0000-0001位图文件的类型:"<<setiosflags(ios::showbase) << hex <<P->bfType << "\n" <<
"0002-0005位图文件的大小(字节):" << hex << P->bfSize <<" = "<<dec<< P->bfSize << "\n" <<
"保留字1:" << hex << P->bfReserved1 << "\n" << "保留字2:" << hex << P->bfReserved2 << "\n" <<
"位图数据的起始位置:" << hex << P->bfOffBits << " = " << dec<< P->bfOffBits <<endl;
std::cout << "-----------------------------信息头---------------------------\n";
std::cout << "000E-0011信息头结构所占的字节数:" << hex << Q->biSize<<" = "<<dec<<Q->biSize << "\n" <<
"0012-0015位图宽度(像素):" << hex << Q->biWidth <<" = "<<dec<<Q->biWidth<<"\n" <<
"0016-0019位图高度(像素):" << hex << Q->biHeight << " = " << dec <<Q->biHeight<< "\n" <<
"001A-001B目标设备的平面数,恒为1:" << hex << Q->biPlanes << "\n" <<
"001C-001D每个像素所占bit数:" << hex << Q->biBitCount << " = " << dec<<Q->biBitCount << "\n" <<
"001E-0021位图压缩类型:" << hex << Q->biCompression << "\n" <<
"0022-0025位图的大小(字节):" << hex << Q->biSizeImage << " = " << dec <<Q->biSizeImage<< "\n" <<
"0026-0029位图水平分辨率(像素每米):" << hex << Q->biXPelsPerMeter << "\n" <<
"002A-002D位图竖直分辨率(像素每米):" << hex << Q->biYPelsPerMeter << "\n" <<
"002E-0031位图实际使用颜色表中的颜色数:" << hex << Q->biClrUsed << "\n" <<
"0032-0035位图显示过程中重要的颜色数" << hex << Q->biClrImportant << endl;
std::cout << "CLAIM[Show_WholeHead]Done.\n"<<"---------------------------------------------------------------"<<endl;
return;
}
void Turn_and_Save(const char* fNAME_=NULL) {
//std::cout << "CALL[Turn_and_Save]Please enter filename out:\n";
//memset(File_Name, 0, maxFileNameLength);
//cin.sync();
//cin >> File_Name;
//for(int i = 87; AddSample[i]; i++) { AddSample[i] = '\0'; }
//ofstream fout(strcat(AddSample, File_Name), ios::binary|ios::in|ios::trunc);
//ofstream fout(File_Name, ios::binary | ios::in | ios::trunc);
ofstream fout(fNAME_, ios::binary | ios::in | ios::trunc);
if(!fout) { cerr << "ERROR[Save_to_new]fout Open failed.\n"; return; }
fout.seekp(ios::beg);
WHOLEHEAD Out(In);
UnitOutput(Out, fout);
if(fout.fail()) { cerr << "ERROR[Save_to_new]in_ output failed.\n" ;
fout.close();return; }
//BYTE zero = 0xFF;
BYTE zero = 0x00;
//****************************************************
for(int i = 0; i < (In.file_info_head.biSize-40); i++) { UnitOutput(Blank[i], fout); }
//适用于V4, V5文件头
//****************************************************
//旋转操作
//参数:
int OldWidth_p = In.file_info_head.biWidth;
int OldWidth_b = fill_to_4(OldWidth_p);
int OldHeight_p = In.file_info_head.biHeight;
int new_Height_p = OldWidth_p;
int new_Width_p = OldHeight_p;
int new_Width_b = fill_to_4(new_Width_p);
const int ByteSinglePixel = In.file_info_head.biBitCount / 8;
int numZero = new_Width_b - ByteSinglePixel * new_Width_p;
std::cout << "CLAIM[Turn_and_Save]numZero is " << numZero << "\n";
int countCompare = 0;
//旋转变换
for(int i = OldWidth_p - 1; i >= 0; i--) {//原列号(像素)
for(int j = 0; j < OldHeight_p; j++) {//原行号
int startIndex = j * (OldWidth_p * ByteSinglePixel) + i * ByteSinglePixel;
for(int l = 0; l < ByteSinglePixel; l++) {
UnitOutput(PixelLine[startIndex+l], fout);
}
++countCompare;
}
for(int k = 0; k < numZero; k++) {
UnitOutput(zero, fout);
}
}
//像素数目不对
if(countCompare != new_Height_p * new_Width_p)
{ cerr << "ERROR[Turn_and_save]Output pixelnum wrong.\n"; goto L; }
//cout << "CLAIM[Save_to_new]Save Succeed at:\n"<
cout << "CLAIM[Save_to_new]Save Succeed.\n";
L: fout.close();
if(PixelLine)delete[]PixelLine;
return;
}
int main(int argc, char**argn) {
Read_Assign_Store(argn[1]);
cout << "CLAIM[main]Store Done.\n";
Turn_and_Save(argn[2]);
cout << "CLAIM[main]Save Done. \n";
return 0;
}
【注】
.bmp文件的位图数据一般是(biHeight取正值时,准确地说)从图片底部向上存储的;
Show_WholeHead()函数有助于检查文件头;
注释掉的部分可实现文件名的手动输入,否则在cmd中传参。