在BREW中使用Gzip压缩来减少应用程序包的大小

    目前在无线增值业务中,涉及图像、声音和数据处理的应用占很大比例,但由于掌上系统的存储能力和处理能力受到很大限制,因此目前BREW应用的大小一般都要求比较小。如果图像在整个应用中占很大比例,那么减小图像的存储空间就成为非常迫切的要求。本文从一个开发者的角度阐述了一种解决存储空间限制的方法,使用Gzip压缩工具结合BREW IUnzipAStrem接口可以将资源的大小减小到原来的1/3左右。

    大多数BREW开发者都回为手机的存储空间问题而苦恼吧,做个简单的小游戏,图片资源也要占个几十上百KB,有人试图用PNG图片来替代BMP图片,但在实际操作的时候会遇到不少的问题,一些底端的设备根本不支持PNG图片,即使支持在使用的时候也经常会遇到一些莫名其妙的问题。大家仔细看看BREW SDK的API文档,不难发现其中有一个IUnzipAStream接口,高通给出的描述如下:IUnzipAStream接口用于扩展IAStream接口,允许将压缩的IAStream解压并以流的方式读取。(此接口只能解压缩使用GZip算法压缩的数据)。

    我的解决方案大致如下:
    1. 使用Gzip压缩工具(Linux下使用GZip,Windows下使用7-Zip)压缩BMP图片、声音、数据等资源;
    2. 把压缩后的文件作为资源加入到.bar文件中;
    3. 在应用程序中使用IUnzipAStream接口来解压相应的数据。
    注:PNG图片不能用GZip算法压缩,因为PNG图片本身已经进行了压缩处理,再用GZip算法压缩不会减少,在一些情况下甚至会变大。

    了解了大体步骤之后我们来简单了解一下GZip算法。GZIP最早由Jean-loup Gailly和Mark Adler创建,用于UNIX系统的文件压缩。我们在Linux中经常会用到后缀为.gz的文件,它们就是GZIP格式的。现今已经成为Internet上使用非常普遍的一种数据压缩格式,或者说一种文件格式。HTTP协议上的GZIP编码是一种用来改进WEB应用程序性能的技术。大流量的WEB站点常常使用GZIP压缩技术来让用户感受更快的速度。
    GZIP文件由1到多个“块”组成,实际上通常只有1块。每个块包含头、数据和尾三部分。块的概貌如下:

  |  ID1  |  ID2  | CM | FLG | MTIME | XFL | OS | 额外的头字段 | 压缩数据 | CRC232 |ISIZE 

   这里强调一下和我们应用有关的块的最后一个字段ISIZE,共4个字节,存储了未压缩前数据的长度,这在解压程序中很重要。

   应用程序中如何解压缩呢?我们看看下面一段解压的代码。

static uint32 UnZip(theApp *pi,int16 picId,byte **ppBuffer)
{
 uint32 size,len,ret;
 void *pSource; //存放压缩图片的源数据
 IMemAStream *memStream;
 IUnzipAStream *unzipStream;
 byte *pSourceBuff; //存放压缩图片的有效数据
 byte *tempBuff; //存放解压图片的数据
 AEEImageInfo info;

 ISHELL_CreateInstance(pi->m_App.m_pIShell,AEECLSID_MEMASTREAM,(void **)&memStream);
 ISHELL_CreateInstance(pi->m_App.m_pIShell,AEECLSID_UNZIPSTREAM,(void **)&unzipStream);

 //载入压缩图片的源数据
 pSource = ISHELL_LoadResDataEx(pi->m_App.m_pIShell,RES_FILE,picId,RESTYPE_IMAGE,NULL,&size);
 size = size-*((byte*)pSource); //计算压缩图片有效数据的长度
 pSourceBuff = (byte*)MALLOC(size);
 MEMCPY(pSourceBuff,(byte*)pSource+*((byte *)pSource),size);  //从源数据中拷贝出有效数据
 ISHELL_FreeResData(pi->m_App.m_pIShell,pSource); //释放载入的源数据
 //把内存流和压缩图片相关联
  IMEMASTREAM_Set(memStream,pSourceBuff,size,0,FALSE);
 //把IUNZIPASTREAM对象设置为从内存流中读取数据
 IUNZIPASTREAM_SetStream(unzipStream,(IAStream *)memStream);
 //计算解压后图片的大小
 len = (*(pSourceBuff+size-4))
  +(*(pSourceBuff+size-3)<<8)
  +(*(pSourceBuff+size-2)<<16)
  +(*(pSourceBuff+size-1)<<24);
 tempBuff = (byte *)MALLOC(len);
 //读取解压图片的数据流到tempBuff
 ret = IUNZIPASTREAM_Read(unzipStream,tempBuff,len);
 if (ret>0)
 {
  //如果没读完就继续读
  while (ret<len)
  {
   ret += IUNZIPASTREAM_Read(unzipStream,(byte*)tempBuff+ret,len-ret);
  }
  IMEMASTREAM_Release(memStream); //释放内存流
  memStream = NULL;
  IUNZIPASTREAM_Release(unzipStream); //释放解压流
  unzipStream = NULL;
  
   //图片转换为位图放在内存中
   *ppBuffer = CONVERTBMP(tempBuff,&info,FALSE);
   FREE(tempBuff);

  return len;
 }
 else if(ret == AEE_STREAM_WOULDBLOCK||ret == 0)
 {
  IMEMASTREAM_Release(memStream);
  memStream = NULL;
  IUNZIPASTREAM_Release(unzipStream);
  unzipStream = NULL;

  if (tempBuff)
  {
   FREE(tempBuff);
  }
  return 0;
 }
 IMEMASTREAM_Release(memStream);
 memStream = NULL;
 IUNZIPASTREAM_Release(unzipStream);
 unzipStream = NULL;

 return 0;
}

   仔细分析这段代码,大概分为如下几步:


1. 用工厂方法产生内存流的指针memStream 和解压流的指针unzipStream;
2. 从资源文件中读取需要解压图片的数据,存放在pSource中;
3. 将有效的压缩图片数据拷贝到pSourceBuff中,并释放pSource;
4. 把内存流memStream和存放压缩图片数据的缓存相关联;
5. 把解压流和内存流相关联,从内存流memStream中读取压缩图片数据;
6. 根据压缩图片数据的最后4个字节计算出解压后图片的大小,并申请内存空间;
7. 从解压流unzipStream中读取解压后的数据存放到tempBuff中;
8. 释放内存流和解压流,用CONVERTBMP宏把解压后的图片数据转换为本地格式存放到ppBuff中;
9. 释放存放解压图片的内存;

   细心一点可能会发现两个问题:
1. 为什么存放压缩图片数据的缓存pSourceBuff没有释放?
2. 存放本地格式位图的缓存ppBuffer事先并没有申请内存空间
  其实当pSourceBuff和内存流memStream关联之后,当调用IMEMASTREAM_Release释放内存流时就会自动释放和流关联的数据缓冲区,不需要手动释放。而CONVERTBMP宏也会自动申请大小适合的内存空间。

   同样声音和数据的解压函数和上面的函数大体相同,只是省去用CONVERTBMP宏进行转换的步骤。

   这种方法对图片的压缩效果非常明显,可以达到PNG图片的压缩效果,对数据文件的压缩效果也比较明显,对声音文件的压缩效果可能要差一些。总体来说,这种方法可以大幅度减少应用程序的大小,而不影响质量,是为存储空间苦恼的开发者的一个不错的选择。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值