Android 压缩样式

今天写这个图片压缩,喜欢的就看一下。

先介绍一下图片存在的几种形式:

1.file文件 

2.流的形式

3.字符串(base64,便于加密)

4.bitmap---内存的形式


---------------------------------------------------------------------------------------------------------------------------------------------------

图片压缩:分为质量压缩,尺寸压缩,采样率压缩,微信压缩(哈夫曼编码)(前三个不是重点,重点是第四种)

一.质量压缩:设置bitmap options属性,降低图片的质量,像素不会减少

/**
 * 1. 质量压缩
   设置bitmap options属性,降低图片的质量,像素不会减少
   第一个参数为需要压缩的bitmap图片对象,第二个参数为压缩后图片保存的位置
   设置options 属性0-100,来实现压缩
 * @param bmp
 * @param file
 */
public static void compressImageToFile(Bitmap bmp,File file) {
    // 0-100 100为不压缩
    int options = 20;
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    // 把压缩后的数据存放到baos中
    bmp.compress(Bitmap.CompressFormat.JPEG, options, baos);
    try {
        FileOutputStream fos = new FileOutputStream(file);
        fos.write(baos.toByteArray());
        fos.flush();
        fos.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

二.尺寸压缩:通过缩放像素减少图片的大小。

/**
 *
 * 2. 尺寸压缩
 通过缩放图片像素来减少图片占用内存大小
 * @param bmp
 * @param file
 */

public static void compressBitmapToFile(Bitmap bmp, File file){
    // 尺寸压缩倍数,值越大,图片尺寸越小
    int ratio = 4;
    // 压缩Bitmap到对应尺寸
    Bitmap result = Bitmap.createBitmap(bmp.getWidth() / ratio, bmp.getHeight() / ratio, Config.ARGB_8888);
    Canvas canvas = new Canvas(result);
    Rect rect = new Rect(0, 0, bmp.getWidth() / ratio, bmp.getHeight() / ratio);
    canvas.drawBitmap(bmp, null, rect, null);

    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    // 把压缩后的数据存放到baos中
    result.compress(Bitmap.CompressFormat.JPEG, 100 ,baos);
    try {
        FileOutputStream fos = new FileOutputStream(file);
        fos.write(baos.toByteArray());
        fos.flush();
        fos.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

三.采样率压缩:通过降低图片像素来减小图片的大小

  /**
        * 设置图片的采样率,降低图片像素
        * @param filePath
        * @param file
        */
       public static void compressBitmap1(InputStream filePath, File file){
           // 数值越高,图片像素越低
           int inSampleSize = 4;
           BitmapFactory.Options options = new BitmapFactory.Options();
//         options.inJustDecodeBounds = false;
           options.inJustDecodeBounds = true;//为true的时候不会真正加载图片,而是得到图片的宽高信息。
           //采样率
           options.inSampleSize = inSampleSize;
           Bitmap bitmap = BitmapFactory.decodeStream(filePath,null ,options);

           ByteArrayOutputStream baos = new ByteArrayOutputStream();
           // 把压缩后的数据存放到baos中
           bitmap.compress(Bitmap.CompressFormat.JPEG, 100 ,baos);
           try {
               if(file.exists())
               {
                   file.delete();
               }
               else {
                   file.createNewFile();
               }
               FileOutputStream fos = new FileOutputStream(file);
               fos.write(baos.toByteArray());
               fos.flush();
               fos.close();
           } catch (Exception e) {
               e.printStackTrace();
           }
       }

四.微信压缩(华为EMUI5.0已经将定常编码改为了哈夫曼编码(猜测,没有看过源码),但是微信是这样子做的)

现在来想一下,IOS拍照1M的图片要比安卓拍照排出来的5M的图片还要清晰(同情景下)?

这就要说到安卓的图片处理引擎了。

95年 JPEG处理引擎,用于最初的在PC上面处理图片的引擎。

05年  skia开源的引擎, 开发了一套基于JPEG处理引擎的第二次开发,便于浏览器的使用。

07年, 安卓诞生,安卓上面用的什么引擎呢?答案是:skia引擎,阉割版。

谷歌拿了skia 思考了半天做了一个决定,去掉一个编码算法---哈夫曼算法。采用定长编码算法。但是解码还是保留了哈夫曼算法。这就导致了图片处理后文件变大了。

而他选择这样做的理由是什么呢?当时由于CPU和内存在手机上都非常吃紧 ,性能差,由于哈夫曼算法非常吃CPU,被迫用了其他的算法。

解释一下:每一个像素包含着ARGB四个信息,即alpha,red,green,blue

现在我们将这四个信息抽象出来,用abcde这五个字母表示一段复杂度为5的信息。在计算机中的表达方式必须按照二进制编码的形式表示。

1.如果通常情况下a,b,c,d,e用以下的方式表达

a:0001

b:0010

c:0011

d:0100

e:0101

大家可以看到,最前面的一位数字都是0,其实是被浪费掉了,

2.所以在定常编码算法下最优的表达方式为:

a:001

b:010

c:011

d:100

e:101

这样做的话就可以节省一位的损耗。(安卓图片处理引擎目前就是使用定常编码算法实现的)

以上是定常编码算法给出的解决方法

3.现在 接下来我们继续进行优化,在哈夫曼算法中我们可以给我们的信息赋予权重。(为信息编码加权)

假设a占据了80%,b占据了10%,c占据了10%,d和e都是0,

a:001(80%)

b:010(10%)

c:011(10%)

d:100

e:101

在这种栗子下,我们可以使用哈夫曼算法进行再次优化为
a:01

b:10

c:11

加权后的abcde:01    10    11 (哈夫曼编码)

定常编码下的abcde:   001  010  011 100 101

加权后的abcde的de呢 当然是没有了,权重为0嘛。
但是问题就在这里了,如何得到每一个字母出现权重呢?如果能够知道每一个权重,那么我们就能够动态的使用最优的编码了。

这里也就是为什么当初谷歌的工程师不使用哈夫曼编码的理由了。

那么如何得到呢?需要去扫描整个信息(即整张图片的信息---每一个像素包括ARGB),要大量的进行计算,非常吃CPU。(可以想象一下,07年你所使用的安卓只能手机)。。。。

一个栗子:1280*720的一张图片需要计算的次数为 720*1280*4(ARGB),这对于当时的安卓手机来说可以是一个灾难。。

以上均是第四种压缩的原理

-------------------如何实现----------=-----------


下载JPEG引擎使用的库---libjpeg库:http://www.ijg.org/


基于该引擎来做一定的开发----自己实现编码。

 NDK编写流程:
1.导入库文件libjpegbither.so
2.导入头文件
3.写mk文件
Android.mk
Applicatoin.mk
4.写代码
C++: XX.cpp
C:   XX.c

编码思维:
1.将android的bitmap解码,并转换成RGB数据
一个图片信息---像素点(argb)
alpha去掉
2.JPEG对象分配空间以及初始化
3.指定压缩数据源
4.获取文件信息
5.为压缩设置参数,比如图像大小、类型、颜色空间
  boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */
6.开始压缩
jpeg_start_compress()
7.压缩结束
jpeg_finish_compress()

目前 ,已经把项目生成了.so文件 可以直接使用,注意NativeUtil(你使用的)所在包名必须和我的项目NativeUtil所在的包名相同。

传送门:https://github.com/yuyunhai/weixinyasuo
 

你可能感兴趣的:(Android 压缩样式)