24位和32位.bmp文件实现图像旋转

.bmp文件的数据结构较为简单, 且24, 32位.bmp文件的位图数据部分不进行压缩,实现图片的旋转是容易的。特别地,旋转90度的整数倍不涉及坐标取整等操作,更为简单。

以下提供笔者近期完成作业的自编代码段,实现对24位或32位.bmp文件顺时针旋转90度:

#include<iostream>
#include<fstream>
#include<cmath>
#include<iomanip>

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 <<setfill('0')<<setw(2)<< (int)(PixelLine[i])<<" "; }

	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]<WHOLEHEAD&>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"<<OutAddress<<" \nwith filename "<<File_Name<<".\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中传参。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值