Jpeg压缩,由于需要进行Huffman编码,所以出现了很多二进制码流。而实际在计算机处理中,都是按照一个字节的来进行存储和处理的。这样就出现了一个很现实的问题-如何
读取和写入二进制码流?
看一段实际的代码(摘自网上)
static Uint8 bytenew=0; // The byte that will be written in the JPG file
static Int8 bytepos=7; //bit position in the byte we write (bytenew)
//should be<=7 and >=0
static Uint16 mask[16]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};
这三个全局变量基本上就是在字节中处理二进制码流的基础。
在这段代码中的存储函数
//********************************************************************
// 方法名称:savebytes
// 方法说明:保存数据流
// 参数说明:
// savestream:欲保存的数据流
// filestream:保存位置
// savelength:该次保存的数据长度
// filelength:文件总长度(用于单增)
//********************************************************************
void savebytes(char *savestream, char *filestream, int savelength, Uint32 *filelength)
{
int i;
int tempfilelength=*filelength;
for(i=0;i<savelength;i++)
{
*(filestream+i+tempfilelength)=*(savestream+i);
(*filelength)++;
}
}
从这个函数中可以看到是 将savestream中的字节拷贝savelength个字节到filestream中去,然后对应更改文件长度
//********************************************************************
// 方法名称:WriteBits
//
// 方法说明:写入二进制流
//
// 参数说明:
// value:AC/DC信号的振幅
//********************************************************************
void WriteBits(HUFFCODE huffCode)
{
WriteBitsStream(huffCode.code,huffCode.length);
}
void WriteBitsSYM2(SYM2 sym)
{
WriteBitsStream(sym.amplitude,sym.codeLen);
}
//********************************************************************
// 方法名称:WriteBitsStream
// 方法说明:写入二进制流
// 参数说明:
// value:需要写入的值
// codeLen:二进制长度
//********************************************************************
void WriteBitsStream(Uint16 value,Int8 codeLen)
{
int posval;//bit position in the bitstring we read, should be<=15 and >=0
posval=codeLen-1;
while (posval>=0)
{
if (value & mask[posval])
{
bytenew|=mask[bytepos];
}
posval--;bytepos--; //bytepos is the position in a byte.
if (bytepos<0)
{
if (bytenew==0xFF)
{
static Uint8 tempbytenew=0;
savebytes((char*)&bytenew,jpegstream,sizeof(bytenew),&length);
savebytes((char*)&tempbytenew,jpegstream,1,&length);
}
else
{
savebytes((char*)&bytenew,jpegstream,sizeof(bytenew),&length);
}
bytepos=7;bytenew=0; // if bytepos get a zero,Set it to 7.
}
}
}
注意:WriteBitsStream中的length是全局变量,代表文件中当前已写入了多少个字节长度。
bytepos指示这个bit在当前这个字节中位置,所以范围是0~7.
而由于Huffman编码的长度不止8bit,也可能会超过8bit,所以由codeLen来表示长度,并且根据codeLen的长度来控制循环。至于为什么要等到bytepos<0,才调用savebytes来存储,当然是因为计算机的存储时以字节为单位的,所以我们不得已只能让bytepos<0来作为完成8个bit的存储的标志。由于程序的这种设计,我们可以看到,如果不到8bit(即一个字节)时,即使调WriteBitsStream,也不会存储二进制数,只是更改相应的bytenew的值,为下次写入bit做准备,直到又一次调用WriteBitsStream时,bytepos终于小于0了累积到了8bit时才写入一个字节,写完后重新置bytepos=7;bytenew=0,重头开始。
函数实际的运行过程:从value中的低bit往高bit位置开始数codeLen个bit,然后从高bit的位置开始取bit,若如果该位数据为1,则将其对应的bytenew这个字节中的权值(相与)的值置1(添加到bytenew中去),bytenew的权值的相关变量bytepos会减一;若该位数据位0,则bytenew的权值的相关变量bytepos仍然会减一,bytenew中该权值对应的位即为0,这样下次则考虑下一位的权值。
例如:
在开始运行时,
第一次调用WriteBitsStream。
如果value值等于0x7e ,codeLen等于0x07。
那么在WriteBitsStream运行完后,bytenew等于0xfc,bytepos等于0x00。而且这两个个值会被保留到下一次WriteBitsStream调用
第二次调用WriteBitsStream。
如果value值等于0xe3,codeLen等于0x08。
那么在WriteBitsStream运行过程中,bytepos在运行在判决bytepos<0时,已经小于0了,这样直接执行
savebytes((char*)&bytenew,jpegstream,sizeof(bytenew),&length);将0xFC存储值jpegstream中。posval也减少了一位。
接着重新开始由bytepos=7;bytenew=0;来进行。
从低到高数到7bit,然后开始从这个高bit的位置往低bit的方向取7bit为0xfc。同理0x000000e3也是从低到高数出9bit,然后从这个高bit的位置往低bit的方向取1bit为0。所以最
终形成一个字节得到0xfc。
注:intel中都是实际存储都是低bit在前,高bit在后的。所谓的big-endian,数据的MSB是最有意义的数据位,存放在低地址位置。