使用方式

  1. 在 project/build.gradle 上添加以下代码

    allprojects {repositories {...maven { url 'https://jitpack.io' }}}
    复制代码
  2. 在 app/build.gradle 添加依赖

    dependencies {implementation 'com.github.yangkun19921001:LIBJPEG_SAMPLE:v1.0.1'
    }
    复制代码
  3. 压缩使用

    //bitmap        : 需要压缩的 bitmap
    //q             : 压缩质量 建议 30 - 50
    //outputFilePath: 压缩之后存储的图片地址
    JpegUtils.native_Compress(Bitmap bitmap,int q,String outputFilePath);
    复制代码

JEPG 是什么?

相信有一部分使用 iPhone 手机用微信发送图片的时候,明明图片大小只有 1M ,但清晰度比 Android 手机 5 M 图片大小的还要清晰,那么这是为什么呢 ?。

当时谷歌开发 Android 的时候,考虑了大部分手机的配置并没有那么高,所以对图片处理使用的是 Skia。当然这个库的底层还是用的 jpeg 图片压缩处理。但是为了能够适配低端的手机(这里的低端是指以前的硬件配置不高的手机,CPU 和内存在手机上都非常吃紧,性能差),由于哈夫曼算法比较吃 CPU 并且编解码慢,被迫用了其他的算法。所以 Skia 在进行图片处理在低版本中并没有开启哈弗曼算法。

那么,JEPG 到底是什么?JEPG (全称是 Joint Photographic Experts Group) 是一种常见的一种图像格式,为什么我在这里会提到 JEPG 呢?是因为开源了一个 C/C++ 库底层是基于哈夫曼算法对图片的压缩 (libjpeg),下面我们就来着重了解下 libjpeg 这个库

libjpeg 简介

libjpeg-turbo 是一个 JPEG 图像编解码器,它使用 SIMD 指令(MMX,SSE2,AVX2,NEON,AltiVec)来加速 x86,x86-64,ARM 和 PowerPC 系统上的基线 JPEG 压缩和解压缩,以及渐进式JPEG 压缩 x86 和 x86-64 系统。在这样的系统上,libjpeg-turbo 的速度通常是 libjpeg 的 2 - 6 倍,其他条件相同。在其他类型的系统上,凭借其高度优化的霍夫曼编码例程,libjpeg-turbo 仍然可以大大超过 libjpeg。在许多情况下,libjpeg-turbo 的性能可与专有的高速 JPEG 编解码器相媲美。 libjpeg-turbo 实现了传统的 libjpeg API 以及功能较弱但更直接的 TurboJPEG API 。 libjpeg-turbo 还具有色彩空间扩展,允许它从/解压缩到32位和大端像素缓冲区(RGBX,XBGR等),以及功能齐全的 Java 接口。 libjpeg-turbo 最初基于 libjpeg / SIMD,这是由 Miyasaka Masaru 开发的 libjpeg v6b 的 MMX 加速衍生物。 TigerVNC 和 VirtualGL 项目在 2009 年对编解码器进行了大量增强,并且在2010年初,libjpeg-turbo 分拆成一个独立项目,目标是为更广泛的用户提供高速 JPEG压缩/解压缩技术。开发人员。

现在我们大概了解到了 libjpeg 是一个对图像编解码库,现在我们需要准备环境去编译 libjpeg。

编译准备工作

系统: Ubuntu 18.04 也可以使用我下载好的 提取码:biyt

libjpeg: libjepg 2.0.2

cmake: cmake-3.14.4-Linux-x86_64.tar.gz

ndk: android-ndk-r17c

开始发车准备编译

  1. ubuntu 中下载 libjpeg

    1. wget github.com/libjpeg-tur…

    2. 解压 tar xvf 2.0.2.tar.gz

    3. 编译参考

  2. ubuntu 中安装 cmake

    1. 删除原来的 apt-get autoremove cmake

    2. wget github.com/Kitware/CMa…

    3. 解压 tar zxvf cmake-3.14.3.tar.gz

    4. 创建软连接

      1. mv cmake-3.14.3-Linux-x86_64 /opt/cmake-3.14.3
      2. ln -sf /opt/cmake-3.14.3/bin/* /usr/bin/

      输入 cmake -- version 如果有这样的显示代表安装成功

  3. 进入到 libjpeg 目录,生成 shell 脚本

    1. vim build.sh 新建一个文件

      # lib-name
      MY_LIBS_NAME=libjpeg-turbo_2.0.2
      # 源码目录
      MY_SOURCE_DIR=/home/yangkun/libjpeg-turbo-2.0.2
      MY_BUILD_DIR=yangkun# android-cmake
      CMAKE_PATH=/opt/cmake-3.14.4/binexport PATH=${CMAKE_PATH}/bin:$PATHNDK_PATH=/home/yangkun//android-ndk-r17c
      BUILD_PLATFORM=linux-x86_64
      TOOLCHAIN_VERSION=4.9
      ANDROID_VERSION=24ANDROID_ARMV5_CFLAGS="-march=armv5te"
      ANDROID_ARMV7_CFLAGS="-march=armv7-a -mfloat-abi=softfp -mfpu=neon"  # -mfpu=vfpv3-d16  -fexceptions -frtti
      ANDROID_ARMV8_CFLAGS="-march=armv8-a"   # -mfloat-abi=softfp -mfpu=neon -fexceptions -frtti
      ANDROID_X86_CFLAGS="-march=i386 -mtune=intel -mssse3 -mfpmath=sse -m32"
      ANDROID_X86_64_CFLAGS="-march=x86-64 -msse4.2 -mpopcnt -m64 -mtune=intel"# params($1:arch,$2:arch_abi,$3:host,$4:compiler,$5:cflags,$6:processor)
      build_bin() {echo "-------------------star build $2-------------------------"ARCH=$1                # arm arm64 x86 x86_64ANDROID_ARCH_ABI=$2    # armeabi armeabi-v7a x86 mips# 最终编译的安装目录PREFIX=$(pwd)/dist/${MY_LIBS_NAME}/${ANDROID_ARCH_ABI}/HOST=$3COMPILER=$4PROCESSOR=$6SYSROOT=${NDK_PATH}/platforms/android-${ANDROID_VERSION}/arch-${ARCH}CFALGS="$5"TOOLCHAIN=${NDK_PATH}/toolchains/${HOST}-${TOOLCHAIN_VERSION}/prebuilt/${BUILD_PLATFORM}# build 中间件BUILD_DIR=./${MY_BUILD_DIR}/${ANDROID_ARCH_ABI}export CFLAGS="$5 -Os -D__ANDROID_API__=${ANDROID_VERSION} --sysroot=${SYSROOT} \-isystem ${NDK_PATH}/sysroot/usr/include \-isystem ${NDK_PATH}/sysroot/usr/include/${HOST} "export LDFLAGS=-pieecho "path==>$PATH"echo "build_dir==>$BUILD_DIR"echo "ARCH==>$ARCH"echo "ANDROID_ARCH_ABI==>$ANDROID_ARCH_ABI"echo "HOST==>$HOST"echo "CFALGS==>$CFALGS"echo "COMPILER==>$COMPILER-gcc"echo "PROCESSOR==>$PROCESSOR"mkdir -p ${BUILD_DIR}   #创建当前arch_abi的编译目录,比如:binary/armeabi-v7acd ${BUILD_DIR}         #此处 进了当前arch_abi的2级编译目录#运行时创建临时编译链文件toolchain.cmake
      cat >toolchain.cmake << EOF
      set(CMAKE_SYSTEM_NAME Linux)
      set(CMAKE_SYSTEM_PROCESSOR $6)
      set(CMAKE_C_COMPILER ${TOOLCHAIN}/bin/${COMPILER}-gcc)
      set(CMAKE_FIND_ROOT_PATH ${TOOLCHAIN}/${COMPILER})
      EOFcmake -G"Unix Makefiles" \-DCMAKE_TOOLCHAIN_FILE=toolchain.cmake \-DCMAKE_POSITION_INDEPENDENT_CODE=1 \-DCMAKE_INSTALL_PREFIX=${PREFIX} \-DWITH_JPEG8=1 \${MY_SOURCE_DIR}make cleanmakemake install#从当前arch_abi编译目录跳出,对应上面的cd ${BUILD_DIR},以便function多次执行cd ../../echo "-------------------$2 build end-------------------------"
      }# build armeabi
      build_bin arm armeabi arm-linux-androideabi arm-linux-androideabi "$ANDROID_ARMV5_CFLAGS" arm#build armeabi-v7a
      build_bin arm armeabi-v7a arm-linux-androideabi arm-linux-androideabi "$ANDROID_ARMV7_CFLAGS" arm#build arm64-v8a
      build_bin arm64 arm64-v8a aarch64-linux-android aarch64-linux-android "$ANDROID_ARMV8_CFLAGS" aarch64#build x86
      build_bin x86 x86 x86 i686-linux-android "$ANDROID_X86_CFLAGS" i386#build x86_64
      build_bin x86_64 x86_64 x86_64 x86_64-linux-android "$ANDROID_X86_64_CFLAGS" x86_64
      复制代码
    2. 如果编译遇见 权限问题

      给它一个 可执行文件的权限 chmod +x build.sh

    3. 继续执行

    4. 编译完成

      这里我们发现 已经有我们需要的 静态库 .a 和 动态库 .so

    5. 在 AndroidStudio 中创建一个简单的项目 用于测试是否压缩成功

      1. 结构目录

      标红的都是重要的文件,include 头文件和 libs/armeabi-v7a 是我们刚刚编译出来的文件

      下面我们就来运行一下看看压缩效果

      1. 压缩主要代码

        jni 代码

        #include <jni.h>
        #include <string>
        #include "../include/jpeglib.h"
        #include <malloc.h>
        #include <android/bitmap.h>void write_JPEG_file(uint8_t *data, int w, int h, jint q, const char *path) {
        //    3.1、创建jpeg压缩对象jpeg_compress_struct jcs;//错误回调jpeg_error_mgr error;jcs.err = jpeg_std_error(&error);//创建压缩对象jpeg_create_compress(&jcs);
        //    3.2、指定存储文件  write binaryFILE *f = fopen(path, "wb");jpeg_stdio_dest(&jcs, f);
        //    3.3、设置压缩参数jcs.image_width = w;jcs.image_height = h;//bgrjcs.input_components = 3;jcs.in_color_space = JCS_RGB;jpeg_set_defaults(&jcs);//开启哈夫曼功能jcs.optimize_coding = true;jpeg_set_quality(&jcs, q, 1);
        //    3.4、开始压缩jpeg_start_compress(&jcs, 1);
        //    3.5、循环写入每一行数据int row_stride = w * 3;//一行的字节数JSAMPROW row[1];while (jcs.next_scanline < jcs.image_height) {//取一行数据uint8_t *pixels = data + jcs.next_scanline * row_stride;row[0] = pixels;jpeg_write_scanlines(&jcs, row, 1);}
        //    3.6、压缩完成jpeg_finish_compress(&jcs);
        //    3.7、释放jpeg对象fclose(f);jpeg_destroy_compress(&jcs);
        }extern "C"
        JNIEXPORT void JNICALL
        Java_com_yk_libjpeg_1sample_libjpeg_JpegUtils_native_1Compress__Landroid_graphics_Bitmap_2ILjava_lang_String_2(JNIEnv *env, jclass type, jobject bitmap, jint q, jstring path_) {const char *path = env->GetStringUTFChars(path_, 0);//从bitmap获取argb数据AndroidBitmapInfo info;//info=new 对象();//获取里面的信息AndroidBitmap_getInfo(env, bitmap, &info);//  void method(list)//得到图片中的像素信息uint8_t *pixels;//uint8_t char    java   byte     *pixels可以当byte[]AndroidBitmap_lockPixels(env, bitmap, (void **) &pixels);//jpeg argb中去掉他的a ===>rgbint w = info.width;int h = info.height;int color;//开一块内存用来存入rgb信息uint8_t *data = (uint8_t *) malloc(w * h * 3);//data中可以存放图片的所有内容uint8_t *temp = data;uint8_t r, g, b;//byte//循环取图片的每一个像素for (int i = 0; i < h; i++) {for (int j = 0; j < w; j++) {color = *(int *) pixels;//0-3字节  color4 个字节  一个点//取出rgbr = (color >> 16) & 0xFF;//    #00rrggbb  16  0000rr   8  00rrggg = (color >> 8) & 0xFF;b = color & 0xFF;//存放,以前的主流格式jpeg    bgr*data = b;*(data + 1) = g;*(data + 2) = r;data += 3;//指针跳过4个字节pixels += 4;}}//把得到的新的图片的信息存入一个新文件 中write_JPEG_file(temp, w, h, q, path);//释放内存free(temp);AndroidBitmap_unlockPixels(env, bitmap);env->ReleaseStringUTFChars(path_, path);
        }
        复制代码

        调用代码

        public class JpegUtils {static {System.loadLibrary("jpeg-yk");}/*** A native method that is implemented by the 'native-lib' native library,* which is packaged with this application.*/public native  static void native_Compress(Bitmap bitmap, int q, String path);}
        复制代码

效果

  1. 开始压缩

        public void click(View view) {File input = new File(Environment.getExternalStorageDirectory(), "/girl.jpg");ImageView preImg = findViewById(R.id.pre);mNextImg = findViewById(R.id.next);inputBitmap = BitmapFactory.decodeFile(input.getAbsolutePath());preImg.setImageBitmap(inputBitmap);JpegUtils .native_Compress(inputBitmap, 10, Environment.getExternalStorageDirectory() + "/girl4.jpg");Toast.makeText(this, "执行完成", Toast.LENGTH_SHORT).show();String filePath = Environment.getExternalStorageDirectory() + "/girl4.jpg";mNextImg.setImageBitmap(BitmapFactory.decodeFile(filePath));}
    复制代码
  2. 动画效果

    压缩效果: 压缩质量在 10 的时候用压缩出来的质量也还是挺好了,只有周围有点点模糊,但是建议压缩质量在 30 -50 之间。

    压缩率: 大约压缩后的图片大小是原图的缩小 6 倍的样子。

资料

更多代码信息请移步 GitHub

提供 libjpeg 编译脚本

libjpeg 编译的源码和动态/静态库 在 dist 目录 提取码:b0cs

计划

图片优化计划出三篇文章

  1. libjpeg 编译及使用
  2. 长图巨图优化
  3. bitmap 内存管理 三级缓存

转载于:https://juejin.im/post/5ce15d0ce51d45106e5e6dac

性能优化 (四) ubuntu 完美编译 libjpeg 图像压缩库,媲美微信图片压缩算法相关推荐

  1. java 优化 寄存器_JVM性能优化系列-(6) 晚期编译优化

    6. 晚期编译优化 晚期编译优化主要是在运行时做的一些优化手段. 6.1 JIT编译器 在部分的商用虚拟机中,java程序最初是通过解释器(Interpreter) 进行解释执行的,当虚拟机发现某个方 ...

  2. Android 性能优化四个方面总结

    目录 一.四个方面 二.卡顿优化 1.Android系统显示原理 2.卡顿根本原因 3.性能分析工具 (1)Profile GPU Rendering (2)TraceView (3)Systrace ...

  3. ubuntu 14 编译视频第三方库ijkplayer,能够在winows下使用

    1.先安装相关环境,详细在这里http://blog.163.com/zhuowr2006@126/blog/static/98334653201612310647799/ 依据上面那个安装之后,会直 ...

  4. iOS之性能优化·提高App的编译速度

    一.前言 经过多年的开发和迭代,我相信很多的 iOS 项目代码已经达到几十万行甚至上百万行的规模,所使用的 Pod 库的数量可以达到几十个甚至上百个,App Store 安装包也变得越来越大,在这么大 ...

  5. MySQL性能优化(四):SQL优化

    一:基础数据准备 DROP TABLE IF EXISTS `tbl_user`; CREATE TABLE `tbl_user` (`id` int(11) NOT NULL AUTO_INCREM ...

  6. 吞吐量达到瓶颈后下降_性能优化四-吞吐量

    说明:以下所有的指标都是在线服务,对于离线服务本人不熟悉. 吞吐量优化通常是在耗时能够满足业务需求的情况下进行优化,不能说单机可以处理1wqps,结果请求rt或者99线达到10s,这样肯定不行. 吞吐 ...

  7. SQL SERVER 性能优化四: 创建分区表

    1.整体介绍 1.1 分区表概念:分区表值得是逻辑上是一个表,物理上被存储到不同的磁盘文件中. 1.2 优势:提高查询性能:提高稳定性:便于管理:对于大数据量表备份更方便. 1.3 建立分区表主要包含 ...

  8. Java性能优化四、性能测试工具

    文章目录 推荐几款常用的性能测试工具 1.ab 2.JMeter 3.LoadRunner 总结 推荐几款常用的性能测试工具 常用的性能测试工具有很多,在这里我将列举几个比较实用的. 对于开发人员来说 ...

  9. 大规模 C++ 编译性能优化系统 OMAX 介绍

    导读:本文探索&研究了大规模C/C++服务编译性能优化相关技术,优化服务性能,降低机器成本,同时为了支持规模推广应用,降低业务线接入成本,保障优化效果,进行面向云上微服务,开展平台化优化服务系 ...

最新文章

  1. SQL Server 2017 AlwaysOn on Linux 配置和维护(18)
  2. MySQL错误1264和1366_Mysql错误1366 - Incorrect integer value解决方法
  3. Android 城市选择,热门城市,全部城市(美团、滴滴、淘宝)
  4. 解决[ERROR] [MY-013276] [Server] Failed to set datadir to ‘F:\Mysql\mysql-8.0.19-winx64\data\‘ (OS er
  5. VS2010中的错误代码号及其含义
  6. java利用随机数简单发牌,!!!!!!!java新手求助,请教一个数组下标越界异常的问题...
  7. 基于蒙特卡洛模型的排队问题求解
  8. 基于阿里云的基础架构设施保障(一)IAAS云计算
  9. 驱动大师服务器获取信息失败,360驱动大师、鲁大师,连测硬件信息都不准,我拿什么信你们...
  10. matlab残差的计算,请教MATLAB中拟合圆或者椭圆的残差如何计算
  11. 集成了谷歌翻译、百度翻译、有道翻译、和金山翻译的小助手软件
  12. 【WAF技巧拓展】————4、web应用防火墙逃逸技术(一)
  13. c java gs_Java基础-gs(垃圾回收)
  14. ThinkPad E450 进入dos界面快捷键 按F1
  15. 朋友圈一杠中间一个点_朋友圈看到这条线,大概率是被删了
  16. Android Animations动画使用详解
  17. FL Studio水果最新版2023安装图文详细教程
  18. 一次应用访问数据库 IP 配成外网 IP 引发的血案
  19. 最新开放的.Co域名注册向导
  20. linux 两个子进程,父进程创建两个子进程,在收到中断信号后杀死两个子进程

热门文章

  1. FBP中的滤波器设计
  2. 解决彩虹六号 2020 第二赛季更新后无法锁亚服
  3. android q微信打不开,微信打不开怎么回事?微信打不开怎么办?
  4. 电魂网络2018年营收达4.48亿元 同比下降10.19%
  5. 单例模式之懒汉与饿汉
  6. python循环嵌套三角形_python使用while、for及循环嵌套实现直角三角形及正、倒金字塔...
  7. qdialog 返回值_QDialog 窗口级别模态(续)
  8. 小程序wxParse不显示图片,图片链接错误不完整,解决方案!
  9. Android Studio执行出现“应用程序安装异常(-15)
  10. 百度字数统计代码研究