上一次我们将24位的皮卡丘旋转了90度,但是后来改需求了。。。要求把32位的.bmp文件也能够旋转90度。上次就懵逼的我继续懵逼,只好继续转向CSDN求助。
![1d94b55028db531090d62c9f6c0090b4.png](https://i-blog.csdnimg.cn/blog_migrate/b771d4601cec5f327e567083f4adf44c.jpeg)
浏览了各种求助帖(还找到了数年前的信科大一学长),终于发现了32位和24位的区别:32位图的每一个像素信息占4个字节,除了RGB三个颜色值以外,还有一个字节存储的是透明度。在读取和写入32位图的时候,每一个像素就要多读(写)一个字节。这就导致了24位和32位的DATA结构体不一样。如果定义两个DATA结构体,之后每次涉及到它的时候都要判断一下图的位数,好像很麻烦的亚子。为了解决这个问题,我们就不得不放弃上一次所用的DATA结构体了 。
//Farewell my DATA~
struct DATA
{
BYTE blue;
BYTE green;
BYTE red;
};
我们转而直接按字节(BYTE)读取,不再将一个像素的信息装在结构体中,而是将其分开读取。需要在原来的代码上做一些改变,先把所有DATA类型的指针改为BYTE类型,分配存储空间的时候把空间大小也调整一下。
//32位就分配size*4,24位就分配size*3
BYTE *imgdata=new BYTE[size * srcInfo.biBitCount / 8];
然后我们把原来代码中的所有数字3改成biBitCount/8:
fread(imgdata + i * w * srcInfo.biBitCount / 8, srcInfo.biBitCount / 8, w, p);
这里因为imgdata的类型已经是unsigned char*了,只占1个字节,所以不用再进行指针类型转换(char*)了。
原先在旋转操作中赋值的时候只用赋值一次,就能将DATA结构体赋值给新的target指针。但现在没有DATA结构体了,我们就需要对每个像素的字节依次赋值:
for (int i=0; i<newH; i++){
for (int j=0; j<newW; j++){
*(target + (i * newW + j)*newInfo.biBitCount / 8) = *(src + (j * w + newH - i - 1)*newInfo.biBitCount / 8);
*(target + (i * newW + j)*newInfo.biBitCount / 8 + 1) = *(src + (j * w + newH - i - 1)*newInfo.biBitCount / 8 + 1);
*(target + (i * newW + j)*newInfo.biBitCount / 8 + 2) = *(src + (j * w + newH - i - 1)*newInfo.biBitCount / 8 + 2);
if (newInfo.biBitCount == 32) {
*(target + (i * newW + j) * newInfo.biBitCount / 8 + 3) =
*(src + (j * w + newH - i - 1) * newInfo.biBitCount / 8 + 3);
}
}
}
这样主体就完成了,但是如果只修改这些的话,会发现最终生成的32位图无法打开。原因在于32位图和24位图的信息头有一点点差异:多了一些不知道是啥的内容。我们就把这些东西放在一个新的结构体Plus里吧。在读取和写入的时候判断一下图像位数,如果是32位就把这个Plus也读/写一下。
struct Plus{
DWORD bV5RedMask;
DWORD bV5GreenMask;
DWORD bV5BlueMask;
DWORD bV5AlphaMask;
DWORD bV5CSType;
CIEXYZTRIPLE bV5Endpoints;
DWORD bV5GammaRed;
DWORD bV5GammaGreen;
DWORD bV5GammaBlue;
DWORD bV5Intent;
DWORD bV5ProfileData;
DWORD bV5ProfileSize;
DWORD bV5Reserved;
};
Plus的内容来自CSDN,其中那个醒目的CIEXYZTRIPLE是啥...我也不知道 。所以只好开始了漫长的搜索之路,最终找到了他的定义:
![c45985dc94bcc0d26f55e266cf378f6b.png](https://i-blog.csdnimg.cn/blog_migrate/28446b4c5475080e50d24035d87d58e4.jpeg)
图中FXPT2DOT30表示它们是带有2位元整数部分和30位元小数部分的定点值,这说的是啥我也不懂,只需要知道FXPT2DOT30其实就和int差不多就好了:
typedef int FXPT2DOT30;
现在就把所有东西全部定义好了,试着转一下这个年代久远的32位图:
![ce7171cd979bc6b7d65c49dcf4411c41.png](https://i-blog.csdnimg.cn/blog_migrate/c39ebc0eaea7ec8ae06bdc5a3fa7b255.jpeg)
![0d8c5e030fc8a74bd51c3ba7c88446e3.png](https://i-blog.csdnimg.cn/blog_migrate/4794d2c05bf7d20078241ca35e8fff73.jpeg)
最后附上完整的代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned int DWORD;
typedef int FXPT2DOT30;
typedef struct tagCIEXYZ
{
FXPT2DOT30 ciexyzX ;
FXPT2DOT30 ciexyzY ;
FXPT2DOT30 ciexyzZ ;
}
CIEXYZ, * LPCIEXYZ ;
typedef struct tagCIEXYZTRIPLE {
CIEXYZ ciexyzRed ;
CIEXYZ ciexyzGreen ;
CIEXYZ ciexyzBlue ;
}CIEXYZTRIPLE;
//位图文件头定义;
struct Header{
DWORD bfSize;//文件大小
WORD bfReserved1;//保留字
WORD bfReserved2;//保留字
DWORD bfOffBits;//从文件头到实际位图数据的偏移字节数
};
//位图信息头定义
struct Info{
DWORD biSize;//信息头大小
DWORD biWidth;//图像宽度
DWORD biHeight;//图像高度
WORD biPlanes;//位平面数,必须为1
WORD biBitCount;//每像素位数
DWORD biCompression; //压缩类型
DWORD biSizeImage; //压缩图像大小字节数
DWORD biXPelsPerMeter; //水平分辨率
DWORD biYPelsPerMeter; //垂直分辨率
DWORD biClrUsed; //位图实际用到的色彩数
DWORD biClrImportant; //本位图中重要的色彩数
};
struct Plus{
DWORD bV5RedMask;
DWORD bV5GreenMask;
DWORD bV5BlueMask;
DWORD bV5AlphaMask;
DWORD bV5CSType;
CIEXYZTRIPLE bV5Endpoints;
DWORD bV5GammaRed;
DWORD bV5GammaGreen;
DWORD bV5GammaBlue;
DWORD bV5Intent;
DWORD bV5ProfileData;
DWORD bV5ProfileSize;
DWORD bV5Reserved;
};
WORD bfType;//文件类型
Header srcHead;//原文件文件头
Info srcInfo;//原文件信息头
Plus srcPlus;
int h,w,size;//原图像的高度、宽度和尺寸
int getDiff(Info & info)
{
int DataSizePerline = (info.biWidth * info.biBitCount+31) / 8;
DataSizePerline -= DataSizePerline % 4;
return DataSizePerline - info.biWidth * info.biBitCount / 8;
}
void rotation(const BYTE* src){
int newH = w;
int newW = h;//图片旋转90度之后宽度、高度互换
int newSize = newH * newW;
FILE *p;
p=fopen("dest.bmp","wb");
Header newHead = srcHead;
Info newInfo = srcInfo;
Plus newPlus = srcPlus;
//修改旋转后图片的尺寸、宽度和高度
newHead.bfSize = (DWORD)(newHead.bfSize);
newInfo.biHeight = (DWORD)newH;
newInfo.biWidth = (DWORD)newW;
int newdiff = getDiff(newInfo);
newInfo.biSizeImage = (DWORD)((newInfo.biWidth * newInfo.biBitCount / 8 + newdiff) * newInfo.biHeight);
//将种类、文件头、信息头写入新bmp文件
fwrite(&bfType,1, sizeof(WORD),p);
fwrite(&newHead,1, sizeof(Header),p);
fwrite(&newInfo,1, sizeof(Info),p);
if (newInfo.biBitCount == 32){
fwrite(&newPlus, 1, sizeof(Plus), p);
}
BYTE* target = new BYTE[newSize * newInfo.biBitCount / 8];
for (int i=0; i<newH; i++){
for (int j=0; j<newW; j++){
*(target + (i * newW + j)*newInfo.biBitCount / 8) = *(src + (j * w + newH - i - 1)*newInfo.biBitCount / 8);
*(target + (i * newW + j)*newInfo.biBitCount / 8 + 1) = *(src + (j * w + newH - i - 1)*newInfo.biBitCount / 8 + 1);
*(target + (i * newW + j)*newInfo.biBitCount / 8 + 2) = *(src + (j * w + newH - i - 1)*newInfo.biBitCount / 8 + 2);
if (newInfo.biBitCount == 32) {
*(target + (i * newW + j) * newInfo.biBitCount / 8 + 3) =
*(src + (j * w + newH - i - 1) * newInfo.biBitCount / 8 + 3);
}
}
}
for (int i=0; i<newH; i++){
fwrite(target + i * newW * (newInfo.biBitCount / 8), newInfo.biBitCount / 8, newW, p);
fseek(p, newdiff, SEEK_CUR);
}
fclose(p);
delete []target;
}
int main(){
FILE* p;
p = fopen("src.bmp", "rb");
if (p != NULL){
//先读取文件类型
fread(&bfType, 1, sizeof(WORD), p);
//读取bmp文件的文件头和信息头
fread(&srcHead,1, sizeof(Header), p);
fread(&srcInfo,1, sizeof(Info), p);
if (srcInfo.biBitCount == 32){
fread(&srcPlus, 1, sizeof(Plus), p);
}
h=srcInfo.biHeight;
w=srcInfo.biWidth;
size = w * h;
BYTE *imgdata=new BYTE[size * srcInfo.biBitCount / 8];
int diff = getDiff(srcInfo);
//读取原图片像素信息
for (int i=0;i<h;i++){
fread(imgdata + i * w * srcInfo.biBitCount / 8, srcInfo.biBitCount / 8, w, p);
fseek(p, diff, SEEK_CUR);
}
fclose(p);
rotation(imgdata);
delete []imgdata;
}
else{
cout<<"无法打开文件"<<endl;
}
return 0;
}