今天工作遇到这种压缩方式,没看太懂,所以就自己查了资料对packbit编码有了个大概的了解,在此记录下来,留给以后的自己!
PackBits是一种快速,简单的无损压缩方案,用于数据的运行长度编码。
苹果公司在Macintosh电脑上推出了MacPaint版本的PackBits格式。这种压缩方案是可以在TIFF文件中使用的压缩类型之一。TGA文件也使用这种RLE压缩方案,但将数据流视为像素而不是字节。
一个PackBits数据流由包含一个字节头的数据包组成。头是有符号的字节; 数据可以是有符号的,无符号的或打包的(例如MacPaint像素)。
在下表中,n是头字节的值,作为有符号整数。
头字节 | 数据在标题字节之后 |
---|---|
0至127 | (1个+ Ñ)文字的数据的字节 |
-1到-127 | 一个字节的数据,在解压缩的输出中重复(1 - n)次 |
-128 | 无操作(跳过并将下一个字节视为标题字节) |
请注意,将0解释为正值或负值不会影响输出。与非运行相邻的两个字节的运行通常写为文字数据。还应该注意的是,根据PackBits数据来确定数据流的结束是没有办法的。也就是说,在读取PackBits数据流之前,必须知道压缩或未压缩数据的大小,以知道它的结束位置。
用自己理解的话讲就是:
pack bit压缩方式是采用头字节计数,后字节记值的方式,第一个字节记录后面有几个重复的字节或者记录后面有几个不重复的字节,记录重复的
用负数表示 ,比如 -1 ff,表示原码是 ff ff ,1代表记录了两个数 (从0开始算)所以解码后就是两个 ff,一个字节记录重复的个数最多记录127个 ,(高位为符号位 有效计数位 只有后面7位 0111 1111 (127)),记录不重复的字节数时用正数来记录个数范围 0 - 127,比如 fe ff f0 fd 这个一共四个字节,编码后就是0x03 fe ff f0 fd ,解码的规则逆推就可以了.
如果有一个字节序列,比如 ff ff ff f0 f0 f0 六个字节,如果用pack bit编码来压缩它 就是前3个ff相同可以编码成 -3 ff,后面3个f0 也是相同的 也可以编码成 -3 f0, 由于计数是从0开始的 所以3个数可以用2来计数,这样压缩结果就是-2 ff -2 ff,用16进制打印就是 FE FF FE F0 (-2 的原码是 1000 0010 符号位不管 反码是 1111 1101 然后反码加1 得到补码 1111 1110 就是FE )
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
static int ZJF_PackBit_Enc_FUN();
static int ZJF_PackBit_Dec_FUN(char *src,int srclen,char *dst,int *dstlen);
static int ZJF_PackBit_Enc_FUN(char *src,int srclen,char *dst,int *dstlen)
{
/*记录相同字节数的个数*/
int SameByteNum = 0;
int outlen = 0;
char * TempDst = NULL; /*记录标记不同字节个数的标记字节的地址 方便数清楚有几个不同字节后去更新个数*/
/*记录结束地址*/
char *End = (src + srclen);
for( ; ; )
{
printf("end = %p(%x) src = %p(%x) \n",End,*End,src,*src);
if(End == src)
{
break;
}
/*取出第一个字节*/
char FirstByte = *src;
char *TempByte = src; /*用于循环*/
SameByteNum = 0;
/*记录有几个字节是相同的*/
for(int i = 0; i < srclen; ++i)
{
printf("FirstByte = %#X TempByte = %#X \n",(unsigned char)FirstByte,(unsigned char)*TempByte);
if(FirstByte == *TempByte)
{
SameByteNum++;
TempByte++;
}
else
{
break;
}
}
/*对相同的字节进行压缩编码,由于packbits压缩是采用标记字节来记录相同字节的个数和不同字节的个数,
若标记字节n在0 - 127(0x7F)之间,则表示后面n个字节是未压缩的,解码时可以直接拷贝,若标记字节n在-1 到 -127 之间
则表示后面有(1 - n)个相同的数据,若n = -128 则跳过它*/
LOOP:
if(SameByteNum > 1)
{
/*第一种情况:相同个数大于128(0 - 127 共有128个数),个数用一个字节来记录,相同的值用一个字节来记录*/
if(SameByteNum > 128)
{
*dst++ = -127;
SameByteNum -= 128;
*dst++ = FirstByte;
outlen++; /*记录输出字节个数*/
src += 128; /*偏移128个字节*/
goto LOOP;
}
else
{
*dst++ = 1 - SameByteNum;
outlen++;
src += SameByteNum;
}
*dst++ = FirstByte;
outlen++;
}
else
{
if(TempDst == NULL) /*表示从新开始记录不同字节*/
{
TempDst = dst;
//*dst += 1;
dst++;
outlen++;
}
else
{
(*TempDst)++; /*每找到一个不相同的字节就要把这个记录不相同字节数的标记字节加1*/
}
*dst++ = FirstByte;
outlen++;
src++;
}
}
*dstlen = outlen;
return 0;
}
/*
srcByte :待重复拷贝的字节
Dst :拷贝的目的地址
cpyNum :需要重复拷贝的个数
*/
static int ZJF_PackBit_SameCpy_FUN(char srcByte,char *Dst,int cpyNum)
{
for(int i = 0 ; i < cpyNum; ++i)
{
*Dst++ = srcByte;
}
return 0;
}
/*
src : 源地址
Dst :拷贝的目的地址
cpyNum :需要拷贝的个数
*/
static int ZJF_PackBit_DifferentCpy_FUN(char *src,char *Dst,int cpyNum)
{
memcpy(Dst,src,cpyNum);
return 0;
}
static int ZJF_PackBit_Dec_FUN(char *src,int srclen,char *dst,int *dstlen)
{
int ByteNum = 0;
int outlen = 0;
/*记录结束地址*/
char *End = (src + srclen);
for( ; ; )
{
printf("end = %p(%x) src = %p(%x) \n",End,*End,src,*src);
if(End == src)
{
break;
}
/*取出第一个字节*/
char FirstByte = *src;
src++; //用完就偏移到下一个地址
if(FirstByte < 0)
{
ByteNum = (1 - FirstByte);
ZJF_PackBit_SameCpy_FUN(*src,dst,ByteNum);
/*记录输出个数*/
outlen += ByteNum;
dst += ByteNum;
src++;
}
else
{
ByteNum = FirstByte + 1;
ZJF_PackBit_DifferentCpy_FUN(src,dst,ByteNum);
src += ByteNum;
dst += ByteNum;
outlen += ByteNum;
}
}
*dstlen = outlen;
return 0;
}
int main( )
{
char *strin = NULL;
char *strout = NULL;
char a = -2;
int ncount = 0;
if(NULL == strin)
{
strin = (char *)malloc(240);
if(!strin)
{
printf("malloc failed \n");
return 0;
}
memset(strin,0,240);
ncount = 2;
strin[0] = 0xD6;
strin[1] = 0xE0;
/*strin[2] = 0xFF;
strin[3] = 0xF0;
strin[4] = 0xF0;
strin[5] = 0xF0;*/
}
if(NULL == strout)
{
strout = (char *)malloc(240);
if(!strout)
{
printf("malloc failed \n");
return 0;
}
memset(strout,0,240);
}
int dstlen = 0;
/*编码*/
ZJF_PackBit_Enc_FUN(strin,ncount,strout,&dstlen);
for(int i = 0; i < dstlen; ++i)
{
printf("pack bit ENC strout[%d] = %#02X(%d) \n",i,(unsigned char)strout[i],strout[i]);
}
/*解码*/
ZJF_PackBit_Dec_FUN(&strout[0],dstlen,&strin[0],&ncount);
for(int i = 0; i < ncount; ++i)
{
printf("pack bit DEC strout[%d] = %#02X(%d) \n",i,(unsigned char)strin[i],strin[i]);
}
return 0;
}