MP是英文Bitmap(位图)的简写,它是Windows操作系统中的标准图像文件格式,能够被多种Windows应用程序所支持。随着Windows操作系统的流行与丰富的Windows应用程序的开发,BMP位图格式理所当然地被广泛应用。这种格式的特点是包含的图像信息较丰富,几乎不进行压缩,但由此导致了它与生俱来的缺点–占用磁盘空间过大。所以,目前BMP在单机上比较流行。
在使用图片时我们经常需要用到旋转,本示例主要完成对BMP图片的顺时针旋转90°和逆时针旋转90°。
[xsw@xsw BMP_stady]$ gcc rivolve.c
[xsw@xsw BMP_stady]$ ./a.out
格式:./a.out <new.bmp> <source.bmp>
[xsw@xsw BMP_stady]$ ./a.out new.bmp watermark.bmp
--------------------顺时针旋转90°----------------------
旋转后图片宽:314
旋转后图片高:504
--------------------逆时针旋转90°----------------------
旋转后图片宽:314
旋转后图片高:504
[xsw@xsw BMP_stady]$
/***********************顺时针旋转90°**************************
**
**形参:const char *new_bmp -- 顺时针90°后图片
** const char *befor_bmp --原始图片
**返回值:0 -- 成功,其他值 -- 失败
**************************************************************/
int BMP_ClockWise_Revolve90(const char *new_bmp,const char *befor_bmp)
{
FILE *fp[2];
fp[0]=fopen(befor_bmp,"rb");
if(fp[0]==NULL)
{
printf("[%s line %d]文件打开失败",__FUNCTION__,__LINE__);
return 1;
}
fp[1]=fopen(new_bmp,"w+b");
if(fp[1]==NULL)
{
printf("[%s line %d]文件打开或创建失败",__FUNCTION__,__LINE__);
return 2;
}
BMP_HEADER bmp_head;
BMP_INFO bmp_info;
fread(&bmp_head,sizeof(BMP_HEADER),1,fp[0]);//读取头数据
if(bmp_head.bfType!=0x4d42)
{
printf("[%s line %d]图片格式错误\n",__FUNCTION__,__LINE__);
fclose(fp[0]);
fclose(fp[1]);
return 3;
}
fwrite(&bmp_head,sizeof(BMP_HEADER),1,fp[1]);//头数据写入到新的文件中
int w,h;//旋转90°宽和高需要互换
fread(&bmp_info,sizeof(BMP_INFO),1,fp[0]);//读取位图数据
h=bmp_info.biHeight;
w=bmp_info.biWidth;
bmp_info.biWidth=h;//旋转后图片宽度
bmp_info.biHeight=w;//旋转后图片高度
fwrite(&bmp_info,sizeof(BMP_INFO),1,fp[1]);//写入位图数据
printf("\n--------------------顺时针旋转90°----------------------\n");
printf("\t旋转后图片宽:%d\n",bmp_info.biWidth);
printf("\t旋转后图片高:%d\n",bmp_info.biHeight);
int befor_oneline_size=w*3;//之前图片一行的字节数
while(befor_oneline_size%4)befor_oneline_size++;//按4字节对齐
int new_oneline_size=bmp_info.biWidth*3;//旋转后图片一行字节数
while(new_oneline_size%4)new_oneline_size++;//按4字节对齐
int i,j;
int offset_count=0;
int rgb=0;
for(i=w-1;i>=0;i--)
{
for(j=0;j<h;j++)
{
offset_count=j*befor_oneline_size+i*3+bmp_head.bfOffBits;//从第一行的最后一个像素点开始
fseek(fp[0],offset_count,SEEK_SET);
fread(&rgb,bmp_info.biBitCount/8,1,fp[0]);
fwrite(&rgb,bmp_info.biBitCount/8,1,fp[1]);
}
if(new_oneline_size>bmp_info.biWidth*3)
{
fwrite(&rgb,new_oneline_size-bmp_info.biWidth*3,1,fp[1]);//补全为4的倍数
}
}
fclose(fp[0]);
fclose(fp[1]);
return 0;
}
/***********************逆时针旋转90°**************************
**
**形参:const char *new_bmp -- 逆时针90°后图片
** const char *befor_bmp --原始图片
**返回值:0 -- 成功,其他值 -- 失败
**************************************************************/
int BMP_antiClockWise_Revolve90(const char *new_bmp,const char *befor_bmp)
{
FILE *fp[2];
fp[0]=fopen(befor_bmp,"rb");
if(fp[0]==NULL)
{
printf("[%s line %d]文件打开失败",__FUNCTION__,__LINE__);
return 1;
}
fp[1]=fopen(new_bmp,"w+b");
if(fp[1]==NULL)
{
printf("[%s line %d]文件打开或创建失败",__FUNCTION__,__LINE__);
return 2;
}
BMP_HEADER bmp_head;
BMP_INFO bmp_info;
fread(&bmp_head,sizeof(BMP_HEADER),1,fp[0]);//读取头数据
if(bmp_head.bfType!=0x4d42)
{
printf("[%s line %d]图片格式错误\n",__FUNCTION__,__LINE__);
fclose(fp[0]);
fclose(fp[1]);
return 3;
}
fwrite(&bmp_head,sizeof(BMP_HEADER),1,fp[1]);//头数据写入到新的文件中
int w,h;//旋转90°宽和高需要互换
fread(&bmp_info,sizeof(BMP_INFO),1,fp[0]);//读取位图数据
h=bmp_info.biHeight;
w=bmp_info.biWidth;
bmp_info.biWidth=h;//旋转后图片宽度
bmp_info.biHeight=w;//旋转后图片高度
fwrite(&bmp_info,sizeof(BMP_INFO),1,fp[1]);//写入位图数据
printf("\n--------------------逆时针旋转90°----------------------\n");
printf("\t旋转后图片宽:%d\n",bmp_info.biWidth);
printf("\t旋转后图片高:%d\n",bmp_info.biHeight);
int befor_oneline_size=w*3;//之前图片一行的字节数
while(befor_oneline_size%4)befor_oneline_size++;//按4字节对齐
int new_oneline_size=bmp_info.biWidth*3;//旋转后图片一行字节数
while(new_oneline_size%4)new_oneline_size++;//按4字节对齐
int i,j;
int offset_count=0;
int rgb=0;
int cnt=0;
unsigned char buff[new_oneline_size];//存放新图片一行字节数
for(i=0;i<w;i++)
{
cnt=0;
for(j=h-1;j>=0;j--)
{
//先读取最后一行的第一个像素点
offset_count=bmp_head.bfOffBits+i*3+j*befor_oneline_size;
fseek(fp[0],offset_count,SEEK_SET);
fread(&rgb,3,1,fp[0]);//读取一个像素点数据
buff[cnt++]=(rgb)&0xff;
buff[cnt++]=(rgb>>8)&0xff;
buff[cnt++]=(rgb>>16)&0xff;
}
fwrite(buff,cnt,1,fp[1]);//将一行颜色数据写入到新文件中
if(cnt!=new_oneline_size)//补全为4的整数倍
{
rgb=0;
fwrite(&rgb,new_oneline_size-cnt,1,fp[1]);
}
}
fclose(fp[0]);
fclose(fp[1]);
return 0;
}
#pragma pack(1) /* 必须在结构体定义之前使用,这是为了让结构体中各成员按1字节对齐*/
/*图片头*/
typedef struct BitMapFileHEADER
{
unsigned short bfType; //保存图片类型。 'BM'
unsigned long bfSize; //图片文件的总大小,以字节为单位(3-6字节,低位在前)
unsigned short bfReserved1;//位图文件保留字,必须为0(7-8字节)
unsigned short bfReserved2;//位图文件保留字,必须为0(9-10字节)
unsigned long bfOffBits; //RGB数据偏移地址,位图数据的起始位置,以相对于位图(11-14字节,低位在前)//文件头的偏移量表示,以字节为单位
}BMP_HEADER;
/*图片信息*/
typedef struct BitMapFileInfo{
unsigned long biSize; //本结构所占用字节数(15-18字节)
unsigned long biWidth; //位图的宽度,以像素为单位(19-22字节)
unsigned long biHeight; //位图的高度,以像素为单位(23-26字节)
unsigned short biPlanes; //目标设备的级别,必须为1(27-28字节)
unsigned short biBitCount; //每个像素所需的位数,必须是1(双色)(29-30字节),4(16色),8(256色)16(高彩色)或24(真彩色)之一
unsigned long biCompression;//位图压缩类型,必须是0(不压缩),(31-34字节)
//1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一
unsigned long biSizeImage; //位图的大小(其中包含了为了补齐行数是4的倍数而添加的空字节),以字节为单位(35-38字节)
unsigned long biXPelsPerMeter;//位图水平分辨率,每米像素数(39-42字节)
unsigned long biYPelsPerMeter;//位图垂直分辨率,每米像素数(43-46字节)
unsigned long biClrUsed; //位图实际使用的颜色表中的颜色数(47-50字节)
unsigned long biClrImportant; //位图显示过程中重要的颜色数(51-54字节)
}BMP_INFO;
int main(int argc,char **argv)
{
if(argc!=3)
{
printf("格式:./a.out \n" );
return 0;
}
int stat=0;
char buff[20];
stat=BMP_ClockWise_Revolve90(argv[1],argv[2]);//顺时针90°
if(stat)
printf("[%s line %d] err %d\n",__FUNCTION__,__LINE__,stat);
else
{
snprintf(buff,sizeof(buff),"eog %s",argv[1]);//字符串拼接
system(buff);//创建进程
}
stat=BMP_antiClockWise_Revolve90(argv[1],argv[2]);//逆时针旋转90°
if(stat)
printf("[%s line %d] err %d\n",__FUNCTION__,__LINE__,stat);
else
{
snprintf(buff,sizeof(buff),"eog %s",argv[1]);//字符串拼接
system(buff);//创建进程
}
return 0;
}
在对BMP图片进行90°旋转时需要注意的点:
1.每一行的字节数,当行字节数不是4的倍数需要用0补齐
2.旋转后的图片的宽=原图片的高;新图片的高=原图片的宽