#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
#include<string.h>
#include"bmp.h"
const unsigned short MaxSize=16384;//14字节最大数
void compress(FILE *,FILE *);//压缩
void decompress(FILE *,FILE *);//解压缩
void wbuffer(FILE *,char *,int);/*将像素信息写入缓冲区*/
void buf_init(char *,int);//缓冲初始化
void rbuffer(FILE *,char *,int);//读缓冲到文件
unsigned long RLE(FILE *,char *,int,unsigned long);//游程压缩,并返回写入字节数
void decode(FILE *,char *,int);//解码,逆运算
DWORD getBMPsize(FILE *ifp);//获取位图大小
int yasuofou(char *,char *,int);//判断压缩可行性,可行性
int main()
{
FILE *ifp,*ofp,*ffp;
char choic;
char *ifilename,*ofilename;
ifilename=(char *)malloc(1);
ofilename=(char *)malloc(1);
printf("a.压缩BMP图像 b.解压BMP图像\n");
choic=getch();
if(choic=='a')
{
printf("请输入压缩(Compress)源文件名:");
scanf("%s",ifilename);
printf("请输入压缩(Compress)输出文件名:");
scanf("%s",ofilename);
if((ifp=fopen(ifilename,"rb"))==NULL)
{
printf("无法打开文件!");
getch();
return 1;
}
if((ofp=fopen(ofilename,"wb"))==NULL)
{
printf("无法创建文件!");
getch();
return 1;
}
compress(ifp,ofp);
printf("\n文件%s成功压缩为文件%s",ifilename,ofilename);
fclose(ifp);
fclose(ofp);
}
else
{
printf("请输入解压缩(Decompress)源文件名:");
scanf("%s",ifilename);
printf("请输入解压缩(Decompress)输出文件名:");
scanf("%s",ofilename);
if((ofp=fopen(ifilename,"rb"))==NULL)
{
printf("无法打开文件!");
getch();
return 1;
}
if((ffp=fopen(ofilename,"wb"))==NULL)
{
printf("无法创建文件!");
getch();
return 1;
}
decompress(ofp,ffp);
printf("\n文件%s成功解压缩为文件%s!",ifilename,ofilename);
fclose(ofp);
fclose(ffp);
}
getch();
return 0;
}
DWORD getBMPsize(FILE *ifp)//打开文件
{
DWORD size;
fseek(ifp,2,0);// fseek(FILE *stream文件指针, long offset偏移量, int fromwhere文件的哪里开始偏移);位置指针指向文件内部的字节位置,随着文件的读取会移动,文件指针如果不重新赋值将不会改变指向别的文件
fread(&size,4,1,ifp);//size_t fread(void*buffer,size_tsize,size_tcount,FILE*stream);1.用于接收数据的地址(指针)(buffer) 2.单个元素的大小(size) :单位是字节而不是位,例如读取一个int型数据就是4个字节 3.元素个数(count) 4.提供数据的文件指针(stream) 返回值:读取的元素的个数
rewind(ifp);//将文件内部的位置指针重新指向一个流(数据流/文件)的开头
return size;//返回大小
}
/**//**************************缓冲区初始化****************************/
void buf_init(char *buffer,int size)
{
memset(buffer,-1,size);//void *memset(void *dest,int c,size_t count );它的作用只是把dest的内存块中的元素的值都设置为c,是0可以不,为啥是-1
}
//BMP压缩
void compress(FILE *ifp,FILE *ofp)
{
unsigned long input,output=0;
input=getBMPsize(ifp);//文件的大小
char buffer[MaxSize];//缓冲区
while(!feof(ifp))//feof(fp)有两个返回值:如果遇到文件结束,函数feof(fp)的值为非零值,否则为0(假是0)
{
wbuffer(ifp,buffer,MaxSize);
output=RLE(ofp,buffer,MaxSize,output);
}
printf("Compressing Rate= %f %% ",output*100.0/input);
}
/**//************************************将二进制的位写入缓冲区***************************************/
void wbuffer(FILE *ifp,char *buffer,int size)
{
char *tpbuf=buffer;//buffer指文件的地址
unsigned char temp=0;
int i;
buf_init(buffer,size);//缓冲区初始化
while(size>0&&!feof(ifp))//当文件大小不为0并且文件没读取完时
{
temp=fgetc(ifp);/**//*每次取8个像素,返回值是读取的一个字节(8位)*/
if(feof(ifp))break;//文件读取完了就跳出循环
for(i=7;i>=0;--i,++tpbuf)
{
*tpbuf=(temp>>i)&1;/**//*写入一行转换为二进制,高位在行左边,不明白*/
}
size-=8;//循环玩一个for语句,文件大小间8
}//一直不断连续读取8个位像素,然后转化为二进制,直至所有的像素都写入缓冲区
}
/**//**********************************对缓冲区中数值进行编码,并返回码序列**************************************/
unsigned long RLE(FILE *ofp,char *buffer,int size,unsigned long output)//output用来确定数据已经压缩过或者没压缩跳过了多少位
{
unsigned short ounit=0,count=0;
int i=0;
char temp;
char *tpbuf=buffer;//文件中的数据的第一个像素的地址赋给tpbuf
temp=0;
while(*tpbuf!=-1&&(i<size-1))//确保不超出缓冲区并且数据有效(数据是-1代表已经完成,超出缓冲)
{
temp=*tpbuf;//将第一个像素的值赋给temp
ounit=count=0;
//currentbit=temp;
if(!yasuofou(tpbuf,buffer,MaxSize))// tpbuf是数据的第一个地址
{
int k=0;
for(k=13;k>=0&&*tpbuf!=-1&&tpbuf!=(buffer+size-1);--k,++tpbuf,++i)//越界了 没有出缓冲区且指针没到14个数据的最后一个地址,
{
count+=(*tpbuf)<<k;//不压缩时把数据读出来14和二进制一个个转到count
}
ounit=count;//不压缩
fwrite(&ounit,sizeof(unsigned short),1,ofp);//把ounit的数据写入到ofp中去,长度是16,左数两位都是0
}
else
{
count=0;
ounit+=1<<15;//压缩 ounit=ounit+1 然后左移15位 将左数第一位置1 代表压缩
ounit+=temp<<14;//将左数第二位置为像素的值
while(*tpbuf==temp&&i<size-1)//当temp和*tpbuf相同时连续读取大于等于16个相同的 直到不相同时跳出来
{
++count;
++tpbuf;//读取一个地址就加1
++i;//i加1确保不超出文件总的大小(size)
}
ounit+=count;//压缩了 比如16个1压缩之后变成1100000000010000,大小已经大大变小了。。。。压缩后的数据都写入在ofp中
fwrite(&ounit,sizeof(unsigned short),1,ofp);//fwrite(s, sizeof(char), strlen(s), pFile);每次写sizeof(char), strlen(s)次
}
output+=sizeof(unsigned short);//返回写入字节数。表示出压缩后的大小。
}
return output;
}
int yasuofou(char *current,char *buffer,int size)
{
char i,id;
int distance=current-buffer;
for(i=0,id=*current;i<16;++i,++distance)
{
if(*(current+i)!=id||*(current+i)==-1||distance>(size-1))
{
return 0;//判断为不可压缩
}
}
return 1;
}
//解压缩
void decompress(FILE *ifp,FILE *ofp)//调用函数时把存储数据地址ofp传递到ifp
{
char buffer[MaxSize];//定义缓冲区
buf_init(buffer,MaxSize);
while(!feof(ifp))
{
decode(ifp,buffer,MaxSize);
rbuffer(ofp,buffer,MaxSize);
}
}
//解码
void decode(FILE *ifp,char *buffer,int size)
{
int i=0;
unsigned short temp=0;
char *tpbuf=buffer;
buf_init(buffer,MaxSize);
while(i<size-1&&!feof(ifp))
{
fread(&temp,sizeof(unsigned short),1,ifp);//用ifp表示存储数据的地址,每次读取16个到temp中
if(feof(ifp)) break;
if(temp>(1<<15))//第一位为1压缩,否则按非压缩处理
{
if(((temp>>14)&1)==1)//第2位为1,则像素为1 为0的话则像素为0
//一定要将操作数移位和1与,不可将1移位和操作数与
{
unsigned short num1=temp&((1<<14)-1);
for(;num1>0&&i<size;--num1,++i,++tpbuf)
*tpbuf=1;
}
else
{
unsigned short num0=temp&((1<<14)-1);
for(;num0>0&&i<size;--num0,++i,++tpbuf)
*tpbuf=0;
}
}
else//非压缩
{
int k;
for(k=13;k>=0&&(tpbuf-buffer)<size-1;--k,++tpbuf,++i)//如果不够14位有几位取几位
{
*tpbuf=(temp>>k)&1;
}
}
}
}
/**//*******************************读取缓冲区中二进制,输出到文件**********************************/
void rbuffer(FILE *ofp,char *buffer,int size)
{
unsigned char temp=0;
int i=0;
char *tpbuf=buffer;
while(size>0&&*tpbuf!=-1)
{
for(i=7;i>=0;--i,++tpbuf)//将位信息转换为1字节整数,并输出到文件
{
if(*tpbuf==-1)break;
temp+=*tpbuf<<i;
--size;
}
fputc(temp,ofp);
temp=0;
}
}