PKU-文件操作作业-BMP图像文件旋转

解题思路:

1.待处理的对象:

待处理的对象是BMP位图,实际上只是一种类型的文件流,和字符流什么的没有区别,这次练习的核心是为了锻炼出惊人的操作读写流的能力,所以,就将其当作01流就可以了。因此关于BMP最重要的就是要理解流的什么地方是什么,实际上文档上已经说得比较详细了。在此不再赘述BMP的基本理论,只重点讨论24位和32位位图。这里的位,实际上就是指一个像素格所占的位数(bit),需要知道的是1字节(Byte)=8bit,因此我们不得不回顾c++基本数据类型的占位。


2.c++基本数据类型的占位

char :1byte = 8bit

short:2byte = 16bit

int :4byte = 32bit

long :4byte = 32bit

所以下面的WORD和DWORD类型其实占位是一样的,可是为了保持文档中的叙述方式,再加上基本数据占位在不同编译器中有所差异,所以就这样吧

...


3.待处理对象的进一步讨论

从作业的文档中提取出来的有用的信息是

(1)BMP有一个文件头,见下面Head类的定义。这一串流占位14个字节(自己数)

(2)BMP有一个信息头,见下面Indo类的定义。这一串流占位40个字节(到这里是不是明白为什么文件头中图像数据的偏移一般是54了?)

(3)图像的像素数据,其中每一个像素格要么是RGB24的要么是RGB32的。RGB24就是一个像素占24位,这也就是普遍流行的24位图,24位是3个字节,分别就是为红、绿、蓝分配的字节。而RGB32多了一个字节,这是为alpha通道分配的,alpha通道是设置透明度的,故RGB32又叫做RGBa。我们只要分别定义这两种像素的基本结构就可以了。其余的颜色表什么的不用理会。在此有一个非常重要的理论,引用文档:“文件存储图像的每一行像素值时,如果存储该行像素值所占的字节数为4的倍数,则正常存储,否则,需要在后端补0,凑足4的倍数。”,拿24位位图来说,一个像素是3byte,如果一张图片的尺寸是37,那么就是111byte每行,按照这个规则,就要补1个0补成112位,如果正好被4整除了,那么就不补。由此我们至少知道两件事情:1、补0很麻烦,会影像流中有用数据的读取以及对转换后的图像数据也需要补0; 2、每行补0数量都是一样的。


4.转置的算法

实际上就是矩阵转置了,只是位图储存规则比较奇葩,倒着存什么的,自己想象加测试应该是没有问题的


5.泛型编程

我的旋转函数用了模板类型,因为处理24位和32位只有像素格的大小不同,其他都是一样的。在此有一个值得注意的地方就是32位位图不会出现补零的情况,套用24位的算法实际上是影像效率的


6.对齐边界

这是一个提示,之前我们了解到Head类型和Info类型的占位分别是14和40,前者不是4的倍数,为了防止读取流的时候Head被读取为16个字节,将对齐边界设置为16.其他值得注意的参考文档中有一处错误“002E-0031:此图像所用的颜色数,如值为0,表示所有颜色一样重要。”,002E-0031应该是002E-0035,从参考资料2可以看出来,不然从000E-0031只有36个字节了,少了4个


7.其他

32位的图像处理我还没有测试过!因为找不到图。。据说可以ps生成。。如果32位的我有错误的地方请一定评论下来

#include
#include
#include
#include

#pragma pack(1)

using namespace std;

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

class Head
{
public:
	WORD type;          // 位图文件的类型,必为16进制的4D42,或10进制的19778
	DWORD size;         // 位图文件整个文件的大小,有用
	WORD r1;            // 保留字1,必为0,无用
	WORD r2;            // 保留字2,必为0,无用
	DWORD offBits;      // 图像数据偏移字节,一般来说是10进制下的54
};

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

class RGB24
{
public:
	RGB24(){ green = 0; red = 0; blue = 0; }
	BYTE green;
	BYTE red;
	BYTE blue;
};

class RGB32: public RGB24
{
public:
	RGB32():RGB24(), alpha(0){};
	BYTE alpha;
};
/* getDiff: 获取每行需要补的0的个数 */
int getDiff(Info& info)     
{
	int t = (info.w * info.bitc + 31) / 8;
	t -= t % 4;
	return  t - info.w * info.bitc / 8;
}

/* Trans: 核心函数,函数模板,BMP转置 */
template
bool Trans(ifstream& src, ofstream& dest, Head& head, Info& info)
{
	Head new_head = head;
	Info new_info = info;
	new_info.h = info.w;
	new_info.w = info.h;

	int diff = getDiff(info);
	T* pic = new T[info.h * info.w];

	for(int i = 0; i < info.h; i++)
	{
		src.read((char*) (pic + i * info.w), info.w * sizeof(T));
		src.seekg(diff, ios::cur);
	}

	diff = getDiff(new_info);
	char* null = new char[diff + 1];
	memset(null, 0, diff + 1);
	new_info.biSizeImage = new_info.h * (new_info.w + diff);
	new_head.size = new_info.biSizeImage + sizeof(new_head) + sizeof(new_info);
	T* new_pic = new T[new_info.w * new_info.h];

	for(int i = 0; i < new_info.h; i++)  
	{
		for(int j = 0; j < new_info.w; j++)
		{
			*(new_pic + (new_info.h - 1 - i) * new_info.w + j) = 
				*(pic + j * info.w + i);
		}
	}
	dest.write((char *) &new_head, sizeof(Head));
	dest.write((char *) &new_info, sizeof(Info));

	for(int i = 0; i < new_info.h; i++)
	{
		dest.write((char*) (new_pic + new_info.w * i), new_info.w * sizeof(T));
		dest.write((char*) null, diff);
	}
	return true;
}

int main(int argc, char* argv[])
{
    char* src_name;
	char* dest_name;
	if(argc == 1)
	{
		cout << "use \"src.bmp\" as default input bmp file name, and \"dest.bmp\" as default ouput 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];
	}
	Head head;
	Info info;
	ifstream src(src_name, ios::in|ios::binary);
	if(!src)
        {
		cout << "文件读取失败~" << endl;
                return 0;
        }
	ofstream dest(dest_name, ios::out|ios::binary);

	src.read((char *) &head, sizeof(Head));
	src.read((char *) &info, sizeof(Info));
	/* 判断BMP的位数,分流 */
	if(info.bitc == 24)
		Trans(src, dest, head, info);
	else
		Trans(src, dest, head, info);
	return 0;
}

你可能感兴趣的:(bmp,c++,位图转换,程序设计实习)