上一次我们将24位的皮卡丘旋转了90度,但是后来改需求了。。。要求把32位的.bmp文件也能够旋转90度。上次就懵逼的我继续懵逼,只好继续转向CSDN求助。
浏览了各种求助帖(还找到了数年前的信科大一学长),终于发现了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是啥...我也不知道 。所以只好开始了漫长的搜索之路,最终找到了他的定义:
图中FXPT2DOT30表示它们是带有2位元整数部分和30位元小数部分的定点值,这说的是啥我也不懂,只需要知道FXPT2DOT30其实就和int差不多就好了:
typedef int FXPT2DOT30;
现在就把所有东西全部定义好了,试着转一下这个年代久远的32位图:
最后附上完整的代码:
#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;//位平面数,必须为1WORD 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;
}