小圈圈 发表于 2016-4-2 18:32:07

[红客焦点]用纯C++实现位图的旋转

本帖最后由 小圈圈 于 2016-4-2 18:51 编辑

目标:用纯C++将位图顺时针旋转90度
位图(Bitmap),顾名思义,就是用一个个像素点表示图片,每个像素点只有一种颜色,它的特点是放大后图像变成一个个色块
关于位图的科普文章大家可以看这篇http://www.cnblogs.com/lzlsky/archive/2012/08/16/2641698.html挺全面的
要实现位图的旋转,就要改变像素的信息,大致分为以下几步
以二进制方式读取位图 ->按顺序获取位图的文件头和信息头 ->分配堆内存获取并存储像素信息->将原图中的每一个像素的信息写入旋转后图片的堆内存对应的位置->写入整个旋转后图片的信息


其中特别强调的是#pragma pack(1)这个预处理指令一定要有,它的功能是让编译器紧凑排列结构体中的内容,比如结构体中有char和int两种变量,正常来说char占1字节而int占4字节。而默认情况下编译器会给char类型分配4个字节的内存,这是为了和后面的int类型融合。加上#pragma pack(1)后就只给变量分配原来的内存了,我们接下来要按位读取数据,这个预处理指令,必须加!!

接下来定义文件头和信息头,用fstream流读取图片,注意ifstream::read方法的第一个参数只能为char*类型,然而我们要读取其他类型的数据呢?不要管他,强制转换为char*即可,这个我试过了。

为原图和旋转后的像素信息分配内存,将位图宽高交换后写入新的文件头,信息头

上面还用到了一个补位函数fill0 ,因为微软规定位图像素信息每行的大小必须是4个字节的倍数,若不是需要用0补齐,我们获取需要补的字节数fill。
我们读取到行尾时跳过fill个字节,写入到行尾时写入fill个字节的0
然后进行位图的旋转,这里需要知道一点,就是位图的像素是按从下到上,从左到右的顺序存储的
比如像素是
A BC D
EFG H
I   JKL
那么它在图片中写入的其实是IJKL EFGH ABCD
旋转后的像素为
IEA
JFB
K GC
L HD
在图片中写入的是LHD KGC JFB IEA
因此旋转图片的代码如下:

最后写入新的像素数据,关闭流指针,别忘了删除new出来的堆内存

然后大功告成了吗?事实上并没有 现在我们只能读取未压缩的图片,压缩后的图片由于信息头和像素数据之间有一段掩码信息,会导致图片损坏
这里有人会说读完信息头后跳过掩码信息,然而掩码信息的大小是不固定的,这怎么办?
细心地人可能注意到了上面有这段代码

我们先读取信息头的大小biSize,然后将流指针返回信息头的初始位置,再读取信息头,读取的大小为biSize 之后发现流指针正好指向像素数据的开始位置
这说明biSize涵盖了原来信息头和后面的掩码信息大小,将掩码信息读入信息头并一并输出即可转换压缩后的图片
附效果图

源代码
特别感谢:error_

admin1964 发表于 2016-4-2 19:19:27

谢谢楼主的分享

Sty,涛 发表于 2016-4-2 20:48:09

admin1964 发表于 2016-4-2 21:45:03

我是来水经验的……

a136 发表于 2016-4-3 03:54:23

支持,看起来还是可以的

asion 发表于 2016-4-3 03:54:24

非常感谢

小龙 发表于 2016-4-3 03:56:40

支持,看起来还是可以的

Micah 发表于 2016-4-3 05:28:07

支持,看起来还是可以的

r00tc4 发表于 2016-4-3 06:15:43

谢谢楼主的分享

admin1964 发表于 2016-4-3 06:36:18

支持,看起来还是可以的
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: [红客焦点]用纯C++实现位图的旋转