• 作业8-24位bmp图片顺时针旋转90度


    昨天上午一次成功写完过于激动,就忘了来写笔记orz

    做作业前很有帮助的几篇文章:

    百度文库-bmp24位位图格式总结

    一个特别厉害的同学知乎写的代码详解(没有用windows.h)

    有一个特别厉害的同学四十几行就写完的代码(用了windows.h)

    然后我的代码就是综合了上面的两者qwq 利用了前者的补足字节函数(稍微修改了一下)和旋转公式(文章里有很形象的图示说明),参考了后者如何应有windows.h,C++二进制文件的读写操作(第一篇用的是C的fopen来读),以及如何实现命令行调用exe文件!(神奇的argc和argv[]原来是这么用的!)

     1 //只能读24位的bmp图片 
     2 #include<iostream>
     3 #include<cstdio>
     4 #include<windows.h>
     5 #include<fstream> 
     6 #include<cstring>
     7 using namespace std;
     8 
     9 int addByte(BITMAPINFOHEADER & info){ //计算每一行要补多少字节,1个字节是8bit,字节数必须是4的整倍数 /32的单位是4byte 
    10     int DataSizePerline = (info.biWidth*info.biBitCount+31)/32*4; //+31是为了除完以后取整 
    11     return DataSizePerline-info.biWidth*info.biBitCount/8;
    12 }
    13 
    14 int main(int argc,char*argv[])
    15 {
    16     ifstream fin(argv[1], ios::binary|ios::in);//argv1是原文件
    17     if (!fin){
    18         cout<<"Source file open error."<<endl;
    19         return 0;
    20     }
    21     ofstream fout(argv[2], ios::binary|ios::out|ios::trunc);
    22     if (!fout){
    23         cout<<"New file open error. ";
    24         return 0;
    25     }
    26         
    27     /*处理文件头和位图信息头*/ 
    28     BITMAPFILEHEADER fileheader;//文件头 
    29     BITMAPINFOHEADER infoheader;//信息头 
    30     fin.read((char*)&fileheader, sizeof(BITMAPFILEHEADER));
    31     fin.read((char*)&infoheader, sizeof(BITMAPINFOHEADER));
    32     fout.write((char*)&fileheader, sizeof(fileheader));//文件头没变化 
    33     int addb = addByte(infoheader); //计算原图片补充字节 
    34     int w =  infoheader.biWidth; 
    35     int h = infoheader.biHeight;  //原图片的宽和高 
    36     
    37     int tmp;
    38     tmp = infoheader.biWidth; 
    39     infoheader.biWidth = infoheader.biHeight; 
    40     infoheader.biHeight = tmp;//交换宽高
    41     
    42     tmp = infoheader.biXPelsPerMeter; 
    43     infoheader.biXPelsPerMeter = infoheader.biYPelsPerMeter; 
    44     infoheader.biYPelsPerMeter = tmp;//交换xy分辨率
    45     
    46     int newaddb = addByte(infoheader); //计算新图片补充字节 
    47     fout.write((char*)&infoheader, sizeof(infoheader));//写入描述信息块
    48     
    49     /*读入原图片像素至imgdata*/ 
    50     int Size =  w*h; //大小 
    51     RGBTRIPLE *imgdata = new RGBTRIPLE[Size]; //读入原图片像素信息 
    52     
    53        for(int i = 0; i < h; i++){
    54            fin.read((char*)imgdata+i*w*3,w*3); //读入一行 
    55            fin.seekg(addb, ios::cur); //跳过补齐的字节 
    56     }
    57     
    58     /*旋转,将旋转完像素存入target*/ 
    59     RGBTRIPLE * target = new RGBTRIPLE[Size];    
    60     int newH = w, newW = h;
    61     for (int i=0; i<newH; i++){
    62         for (int j=0; j<newW; j++){
    63             *(target+i*newW+j) = *(imgdata+j*w+newH-i-1);
    64         }
    65     }
    66     
    67     /*将target写入文件*/ 
    68     char * ab = new char[newaddb]; 
    69     memset(ab, 0, sizeof(ab));//用来补入0 
    70     for (int i=0; i<newH; i++){
    71         fout.write((char*)target+i*newW*3, 3*newW);
    72      fout.write(ab, newaddb);
    73     }
    74     delete[]imgdata;
    75     delete[]target;
    76     delete[]ab;
    77     fin.close();
    78     fout.close();
    79     cout<<"Rotate successfully!"<<endl;
    80     return 0;
    81 }

    其实注释很详尽了。其实只要先了解一下bmp文件的格式,就发现它只包含三部分:文件头、图片信息头、图片像素信息。

    我们要做的只有三件事,把文件头原封不动地写到新文件里、把图片信息头里的有关宽和高的信息交换一下直接写到新文件里,然后读入图片像素信息(注意跳过用来补足的字节),旋转之后写入新文件(要补足字节)。

    关于补足字节再说明一下吧,公式看起来不是很容易理解,我也是在网上的犄角旮旯找到了一个很好的说明:

     DataSizePerline = (info.biWidth*info.biBitCount+31)/32*4; 

    这一行info.biWidth*info.biBitCount是计算图片一行要占多少位(biWidth的单位是RGPTRIPLE)每一行的字节数需要是4的整数倍,所以最小单位是4*8是32bit,那么一行如果不能整除32,最多是差31bit,/是向下取整,所以这样算出来的是这一行需要有几个【四字节】,但单位是字节,所以再乘个4,这样算出来的就是这一行需要有多少字节,并且这样算出来的肯定是4的整数倍。那么要补充的字节就是DataSizePerline-info.biWidth*info.biBitCount/8

    还有一个问题就是怎么通过命令行调用这个exe文件,就是通过main函数的参数argc和argv。具体调用方法就是cmd进入命令行然后【进入到exe文件所在的文件夹】,然后rotatebmp 1.bmp myout.bmp,其中rotatebmp是exe文件的文件名,1.bmp是源文件,myout.bmp是新图片

  • 相关阅读:
    java实现前n项和,要求不使用循环、乘除法、判断标识
    java 线程池 带返回值
    java 多线程 数据通信
    jedis使用分布式锁
    记一次自定义管理工厂使用spring自动装载bean
    面试题玩数组
    记一次随便排序算法
    九九乘法表打印记一次al面试
    多线程操作共享变量顺序输出abc 记一次al面试题
    博客迁移
  • 原文地址:https://www.cnblogs.com/fangziyuan/p/12659747.html
Copyright © 2020-2023  润新知