性能优化---Bitmap内存管理及优化

                性能优化---Bitmap内存管理及优化

图片存在的几种形式:

    File

    流的形式

    Bitmap的形式---内存

图片压缩的几种方式:

1.质量压缩

原理:通过算法扣掉(同化)图片中的一些某个些点附近相近的像素,达到降低质量减少文件大小的目的。

牺牲:减小了图片质量。

注意:它其实只能实现对file的影响,对加载这个图片出来的bitmap内存是无法节省,还是那么大。

因为bitmap在内存的大小是按照像素计算的,也就是width*height 对质量压缩并不会改变图片真实的像素。

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

 /**
            * 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.尺寸压缩

特点:通过减少单位的尺寸的像素值,真正意义上的降低像素。1020*1080--

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

  *
            * 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();
               }
           }
    }

3.采样率压缩

设置图片的采样率,降低图片像素


            * 设置图片的采样率,降低图片像素
            * @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();
               }
           }

1.IOS拍照1M的图片比Android拍照出来5M的图片还要清晰。

都是在同一个环境下,保存的都是JPEG。

 

为什么?

 

2.图像处理引擎

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

 

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

览器的使用。

 

07年, 安卓上面用的什么引擎?

    skia引擎,阉割版。

gogole拿了skia思考了半天做了一个决定,去掉一个编码算法---哈夫曼算法。采用定长编码算法。

但是解码还是保留了哈夫曼算法。

导致可图片处理后文件变大了。

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

 

我们的优化:

绕过Android bitmap API层,来自己编码实现---修复使用哈夫曼算法。

如何实现?

下载JPEG引擎使用的库---libjpeg库

http://www.ijg.org/

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

核心代码JNI

#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");
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值