美文网首页
Bitmap优化

Bitmap优化

作者: 左上偏右 | 来源:发表于2016-12-04 20:18 被阅读502次

    网络的优化

    1.请求的缓存。
    Http请求是可以做缓存的。

    三级缓存
    内存缓存、外部缓存
    算法LruCache
    +自定的 一周过期等等。
    Sqlite缓存+加密


    BitmapFactory.Options:参数:
    inDensity:bitmap的像素密度
    inTargetDensity:bitmap最终的像素密度
    DensityDpi(屏幕的像素密度) 分辨率 Density
    160dpi 320x533 1
    px = dp*Density

    手机像素表

    图片存在的几种形式:

    • File
    • 流的形式
    • Bitmap的形式---内存

    图片压缩方式

    1.质量压缩

    AB
    CD
    压缩后
    AA
    AA
    原理:通过算法抠掉(同化)了图片中的一些某个些点附近相近的像素,达到降低质量介绍文件大小的目的。
    减小了图片质量
    ** 注意:*它其实只能实现对file的影响,对加载这个图片出来的bitmap内存是无法节省的,还是那么大。 因为bitmap在内存中的大小是按照像素计算的,也就是widthheight,对于质量压缩,并不会改变图片的真实的像素(像素大小不会变)。

    ** 使用场景:**
    将图片压缩后保存到本地,或者将图片上传到服务器。根据实际需求来。

    public static void compressImageToFile(Bitmap bmp,File file){
        //0~100
        int quality = 50;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bmp.compress(Bitmap.CompressFormat.JPEG, quality , baos );
        try {
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(baos.toByteArray());
            fos.flush();
            fos.close();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
    2.尺寸压缩

    通过减少单位尺寸的像素值,真正意义上的降低像素。1020*8880

    ** 使用场景:** 缓存缩略图的时候(头像处理)

    public static void compressBitmapToFileBySize(Bitmap bmp,File file){
        //压缩尺寸倍数,值越大,图片的尺寸就越小
        int ratio = 4;
        Bitmap result = Bitmap.createBitmap(bmp.getWidth()/ratio, 
                  bmp.getHeight()/ratio, Bitmap.Config.ARGB_8888);
        
        Canvas canvas = new Canvas(result);
        RectF rect = new RectF(0, 0, bmp.getWidth()/ratio, bmp.getHeight()/ratio);
        canvas.drawBitmap(bmp, null, rect , null);
        
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        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();
        }
        
    }
    
    3.采样率压缩
    4.终极压缩(NDK层压缩)

    1.IOS拍照1M的图片要比安卓拍照排出来的5M的图片还要清晰。
    都是在同一个环境下,保存的都是JPEG。为什么?

    2.图像处理引擎
    95年 JPEG处理引擎,用于最初的在PC上面处理图片的引擎。
    05年 skia开源的引擎, 开发了一套基于JPEG处理引擎的第二次开发。便于浏览器的使用。

    07年, 安卓上面用的什么引擎
    skia引擎,阉割版
    谷歌拿了skia 思考了半天做了一个决定,去掉一个编码算法---哈夫曼算法。采用定长编码算法。但是解码还是保留了哈夫曼算法。导致了图片处理后文件变大了。

    理由:当时由于CPU和内存在手机上都非常吃紧 性能差,而哈夫曼算法非常吃CPU,被迫用了其他的算法。

    我们的优化:
    绕过安卓Bitmap API层,来自己编码实现----修复使用哈夫曼算法。

    argb
    一个像素点包涵四个信息:alpha,red,green,blue

    a b c d e

    abcde acdbe bacde ……

    101010100011100
    a:001
    b:010
    c:011
    d:100
    e:101
    用3位来表示一个字符信息,属于定长编码的最优。

    abcde
    001 010 011 100 101
    加权信息编码

    a:80%
    b:10%
    c:10%
    d:0%
    e:0%
    这种情况,编码就可以优化了
    a:01
    b:10
    c:11
    优化后的abc:01 10 11
    优化前的abc:001 010 011

    通过加权可以知道那个信息为0(即没有),那么就表示可以省略“一位”,这样数据量信息就会减少,从而使图片变得更小。

    那么如何得到每一个字母出现的权重?
    哈夫曼编码:需要去扫描每个信息(图片信息--每一个像素包括ARGB),需要大量计算,很吃CPU。
    1280800像素4

    如何实现?

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

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

    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()
    8.释放资源


    /*
     * Copyright 2014 http://Bither.net
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *    http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package net.bither.util;
    
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileOutputStream;
    
    import android.graphics.Bitmap;
    import android.graphics.Bitmap.Config;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Rect;
    import android.util.Log;
    
    public class NativeUtil {
         private static int DEFAULT_QUALITY = 95;
    
            /**
             * @param bit      bitmap对象
             * @param fileName 指定保存目录名
             * @param optimize 是否采用哈弗曼表数据计算 品质相差5-10倍
             * @Description: JNI基本压缩
             */
            public static void compressBitmap(Bitmap bit, String fileName, boolean optimize) {
                saveBitmap(bit, DEFAULT_QUALITY, fileName, optimize);
            }
    
            /**
             * @param image    bitmap对象
             * @param filePath 要保存的指定目录
             * @Description: 通过JNI图片压缩把Bitmap保存到指定目录
             */
            public static void compressBitmap(Bitmap image, String filePath) {
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                // 质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
                int options = 20;
                // JNI调用保存图片到SD卡 这个关键
                NativeUtil.saveBitmap(image, options, filePath, true);
            }
    
            /**
             * 计算缩放比
             *
             * @param bitWidth  当前图片宽度
             * @param bitHeight 当前图片高度
             * @return
             * @Description:函数描述
             */
            public static int getRatioSize(int bitWidth, int bitHeight) {
                // 图片最大分辨率
                int imageHeight = 1920;
                int imageWidth = 1080;
                // 缩放比
                int ratio = 1;
                // 缩放比,由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
                if (bitWidth > bitHeight && bitWidth > imageWidth) {
                    // 如果图片宽度比高度大,以宽度为基准
                    ratio = bitWidth / imageHeight;
                } else if (bitWidth < bitHeight && bitHeight > imageHeight) {
                    // 如果图片高度比宽度大,以高度为基准
                    ratio = bitHeight / imageHeight;
                }
                // 最小比率为1
                if (ratio <= 0)
                    ratio = 1;
                return ratio;
            }
    
            /**
             * 调用native方法
             *
             * @param bit
             * @param quality
             * @param fileName
             * @param optimize
             * @Description:函数描述
             */
            public static void saveBitmap(Bitmap bit, int quality, String fileName, boolean optimize) {
                compressBitmap(bit, bit.getWidth(), bit.getHeight(), quality, fileName.getBytes(), optimize);
            }
    
            /**
             * 调用底层 bitherlibjni.c中的方法
             *
             * @param bit
             * @param w
             * @param h
             * @param quality
             * @param fileNameBytes
             * @param optimize
             * @return
             * @Description:函数描述
             */
            public static native String compressBitmap(Bitmap bit, int w, int h, int quality, byte[] fileNameBytes,
                                                        boolean optimize);
    
            /**
             * 加载lib下两个so文件
             */
            static {
                System.loadLibrary("jpegbither");
                System.loadLibrary("bitherjni");
            }
    
    
            /**
             * 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 = 8;
                // 压缩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 compressBitmap(String filePath, File file){
                // 数值越高,图片像素越低
                int inSampleSize = 8;
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inJustDecodeBounds = false;
    //          options.inJustDecodeBounds = true;//为true的时候不会真正加载图片,而是得到图片的宽高信息。
                //采样率
                options.inSampleSize = inSampleSize;
                Bitmap bitmap = BitmapFactory.decodeFile(filePath, 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();
                }
            }
    }
    
    
    #include "bitherlibjni.h"
    #include <string.h>
    #include <android/bitmap.h>
    #include <android/log.h>
    #include <stdio.h>
    #include <setjmp.h>
    #include <math.h>
    #include <stdint.h>
    #include <time.h>
    
    //统一编译方式
    extern "C" {
    #include "jpeg/jpeglib.h"
    #include "jpeg/cdjpeg.h"        /* Common decls for cjpeg/djpeg applications */
    #include "jpeg/jversion.h"      /* for version message */
    #include "jpeg/android/config.h"
    }
    
    
    #define LOG_TAG "jni"
    #define LOGW(...)  __android_log_write(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
    #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
    #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
    
    #define true 1
    #define false 0
    
    typedef uint8_t BYTE;
    
    char *error;
    struct my_error_mgr {
      struct jpeg_error_mgr pub;
      jmp_buf setjmp_buffer;
    };
    
    typedef struct my_error_mgr * my_error_ptr;
    
    METHODDEF(void)
    my_error_exit (j_common_ptr cinfo)
    {
      my_error_ptr myerr = (my_error_ptr) cinfo->err;
      (*cinfo->err->output_message) (cinfo);
      error=(char*)myerr->pub.jpeg_message_table[myerr->pub.msg_code];
      LOGE("jpeg_message_table[%d]:%s", myerr->pub.msg_code,myerr->pub.jpeg_message_table[myerr->pub.msg_code]);
     // LOGE("addon_message_table:%s", myerr->pub.addon_message_table);
    //  LOGE("SIZEOF:%d",myerr->pub.msg_parm.i[0]);
    //  LOGE("sizeof:%d",myerr->pub.msg_parm.i[1]);
      longjmp(myerr->setjmp_buffer, 1);
    }
    
    int generateJPEG(BYTE* data, int w, int h, int quality,
            const char* outfilename, jboolean optimize) {
    
        //jpeg的结构体,保存的比如宽、高、位深、图片格式等信息,相当于java的类
        struct jpeg_compress_struct jcs;
    
        //当读完整个文件的时候就会回调my_error_exit这个退出方法。setjmp是一个系统级函数,是一个回调。
        struct my_error_mgr jem;
        jcs.err = jpeg_std_error(&jem.pub);
        jem.pub.error_exit = my_error_exit;
        if (setjmp(jem.setjmp_buffer)) {
            return 0;
        }
    
        //初始化jsc结构体
        jpeg_create_compress(&jcs);
        //打开输出文件 wb:可写byte
        FILE* f = fopen(outfilename, "wb");
        if (f == NULL) {
            return 0;
        }
        //设置结构体的文件路径
        jpeg_stdio_dest(&jcs, f);
        jcs.image_width = w;//设置宽高
        jcs.image_height = h;
    //  if (optimize) {
    //      LOGI("optimize==ture");
    //  } else {
    //      LOGI("optimize==false");
    //  }
    
        //看源码注释,设置哈夫曼编码:/* TRUE=arithmetic coding, FALSE=Huffman */
        jcs.arith_code = false;
        int nComponent = 3;
        /* 颜色的组成 rgb,三个 # of color components in input image */
        jcs.input_components = nComponent;
        //设置结构体的颜色空间为rgb
        jcs.in_color_space = JCS_RGB;
    //  if (nComponent == 1)
    //      jcs.in_color_space = JCS_GRAYSCALE;
    //  else
    //      jcs.in_color_space = JCS_RGB;
    
        //全部设置默认参数/* Default parameter setup for compression */
        jpeg_set_defaults(&jcs);
        //是否采用哈弗曼表数据计算 品质相差5-10倍
        jcs.optimize_coding = optimize;
        //设置质量
        jpeg_set_quality(&jcs, quality, true);
        //开始压缩,(是否写入全部像素)
        jpeg_start_compress(&jcs, TRUE);
    
        JSAMPROW row_pointer[1];
        int row_stride;
        //一行的rgb数量
        row_stride = jcs.image_width * nComponent;
        //一行一行遍历
        while (jcs.next_scanline < jcs.image_height) {
            //得到一行的首地址
            row_pointer[0] = &data[jcs.next_scanline * row_stride];
    
            //此方法会将jcs.next_scanline加1
            jpeg_write_scanlines(&jcs, row_pointer, 1);//row_pointer就是一行的首地址,1:写入的行数
        }
        jpeg_finish_compress(&jcs);//结束
        jpeg_destroy_compress(&jcs);//销毁 回收内存
        fclose(f);//关闭文件
    
        return 1;
    }
    
    /**
     * byte数组转C的字符串
     */
    char* jstrinTostring(JNIEnv* env, jbyteArray barr) {
        char* rtn = NULL;
        jsize alen = env->GetArrayLength( barr);
        jbyte* ba = env->GetByteArrayElements( barr, 0);
        if (alen > 0) {
            rtn = (char*) malloc(alen + 1);
            memcpy(rtn, ba, alen);
            rtn[alen] = 0;
        }
        env->ReleaseByteArrayElements( barr, ba, 0);
        return rtn;
    }
    
    jstring Java_net_bither_util_NativeUtil_compressBitmap(JNIEnv* env,
            jclass thiz, jobject bitmapcolor, int w, int h, int quality,
            jbyteArray fileNameStr, jboolean optimize) {
        BYTE *pixelscolor;
        //1.将bitmap里面的所有像素信息读取出来,并转换成RGB数据,保存到二维byte数组里面
        //处理bitmap图形信息方法1 锁定画布
        AndroidBitmap_lockPixels(env,bitmapcolor,(void**)&pixelscolor);
    
        //2.解析每一个像素点里面的rgb值(去掉alpha值),保存到一维数组data里面
        BYTE *data;
        BYTE r,g,b;
        data = (BYTE*)malloc(w*h*3);//每一个像素都有三个信息RGB
        BYTE *tmpdata;
        tmpdata = data;//临时保存data的首地址
        int i=0,j=0;
        int color;
        for (i = 0; i < h; ++i) {
            for (j = 0; j < w; ++j) {
                //解决掉alpha
                //获取二维数组的每一个像素信息(四个部分a/r/g/b)的首地址
                color = *((int *)pixelscolor);//通过地址取值
                //0~255:
    //          a = ((color & 0xFF000000) >> 24);
                r = ((color & 0x00FF0000) >> 16);
                g = ((color & 0x0000FF00) >> 8);
                b = ((color & 0x000000FF));
                //改值!!!----保存到data数据里面
                *data = b;
                *(data+1) = g;
                *(data+2) = r;
                data = data + 3;
                //一个像素包括argb四个值,每+4就是取下一个像素点
                pixelscolor += 4;
            }
        }
        //处理bitmap图形信息方法2 解锁
        AndroidBitmap_unlockPixels(env,bitmapcolor);
        char* fileName = jstrinTostring(env,fileNameStr);
        //调用libjpeg核心方法实现压缩
        int resultCode = generateJPEG(tmpdata,w,h,quality,fileName,optimize);
        if(resultCode ==0){
            jstring result = env->NewStringUTF("-1");
            return result;
        }
        return env->NewStringUTF("1");
    }
    
    

    相关文章

      网友评论

          本文标题:Bitmap优化

          本文链接:https://www.haomeiwen.com/subject/bckhmttx.html