性能优化 (四) ubuntu 完美编译 libjpeg 图像压缩库,媲美微信图片压缩算法
使用方式
在 project/build.gradle 上添加以下代码
allprojects {repositories {...maven { url 'https://jitpack.io' }}} 复制代码
在 app/build.gradle 添加依赖
dependencies {implementation 'com.github.yangkun19921001:LIBJPEG_SAMPLE:v1.0.1' } 复制代码
压缩使用
//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
开始发车准备编译
ubuntu 中下载 libjpeg
wget github.com/libjpeg-tur…
解压 tar xvf 2.0.2.tar.gz
编译参考
ubuntu 中安装 cmake
删除原来的 apt-get autoremove cmake
wget github.com/Kitware/CMa…
解压 tar zxvf cmake-3.14.3.tar.gz
创建软连接
- mv cmake-3.14.3-Linux-x86_64 /opt/cmake-3.14.3
- ln -sf /opt/cmake-3.14.3/bin/* /usr/bin/
输入 cmake -- version 如果有这样的显示代表安装成功
进入到 libjpeg 目录,生成 shell 脚本
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 复制代码
如果编译遇见 权限问题
给它一个 可执行文件的权限 chmod +x build.sh
继续执行
编译完成
这里我们发现 已经有我们需要的 静态库 .a 和 动态库 .so
在 AndroidStudio 中创建一个简单的项目 用于测试是否压缩成功
- 结构目录
标红的都是重要的文件,include 头文件和 libs/armeabi-v7a 是我们刚刚编译出来的文件
下面我们就来运行一下看看压缩效果
压缩主要代码
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);} 复制代码
效果
开始压缩
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));} 复制代码
动画效果
压缩效果: 压缩质量在 10 的时候用压缩出来的质量也还是挺好了,只有周围有点点模糊,但是建议压缩质量在 30 -50 之间。
压缩率: 大约压缩后的图片大小是原图的缩小 6 倍的样子。
资料
更多代码信息请移步 GitHub
提供 libjpeg 编译脚本
libjpeg 编译的源码和动态/静态库 在 dist 目录 提取码:b0cs
计划
图片优化计划出三篇文章
- libjpeg 编译及使用
- 长图巨图优化
- bitmap 内存管理 三级缓存
转载于:https://juejin.im/post/5ce15d0ce51d45106e5e6dac
性能优化 (四) ubuntu 完美编译 libjpeg 图像压缩库,媲美微信图片压缩算法相关推荐
- java 优化 寄存器_JVM性能优化系列-(6) 晚期编译优化
6. 晚期编译优化 晚期编译优化主要是在运行时做的一些优化手段. 6.1 JIT编译器 在部分的商用虚拟机中,java程序最初是通过解释器(Interpreter) 进行解释执行的,当虚拟机发现某个方 ...
- Android 性能优化四个方面总结
目录 一.四个方面 二.卡顿优化 1.Android系统显示原理 2.卡顿根本原因 3.性能分析工具 (1)Profile GPU Rendering (2)TraceView (3)Systrace ...
- ubuntu 14 编译视频第三方库ijkplayer,能够在winows下使用
1.先安装相关环境,详细在这里http://blog.163.com/zhuowr2006@126/blog/static/98334653201612310647799/ 依据上面那个安装之后,会直 ...
- iOS之性能优化·提高App的编译速度
一.前言 经过多年的开发和迭代,我相信很多的 iOS 项目代码已经达到几十万行甚至上百万行的规模,所使用的 Pod 库的数量可以达到几十个甚至上百个,App Store 安装包也变得越来越大,在这么大 ...
- MySQL性能优化(四):SQL优化
一:基础数据准备 DROP TABLE IF EXISTS `tbl_user`; CREATE TABLE `tbl_user` (`id` int(11) NOT NULL AUTO_INCREM ...
- 吞吐量达到瓶颈后下降_性能优化四-吞吐量
说明:以下所有的指标都是在线服务,对于离线服务本人不熟悉. 吞吐量优化通常是在耗时能够满足业务需求的情况下进行优化,不能说单机可以处理1wqps,结果请求rt或者99线达到10s,这样肯定不行. 吞吐 ...
- SQL SERVER 性能优化四: 创建分区表
1.整体介绍 1.1 分区表概念:分区表值得是逻辑上是一个表,物理上被存储到不同的磁盘文件中. 1.2 优势:提高查询性能:提高稳定性:便于管理:对于大数据量表备份更方便. 1.3 建立分区表主要包含 ...
- Java性能优化四、性能测试工具
文章目录 推荐几款常用的性能测试工具 1.ab 2.JMeter 3.LoadRunner 总结 推荐几款常用的性能测试工具 常用的性能测试工具有很多,在这里我将列举几个比较实用的. 对于开发人员来说 ...
- 大规模 C++ 编译性能优化系统 OMAX 介绍
导读:本文探索&研究了大规模C/C++服务编译性能优化相关技术,优化服务性能,降低机器成本,同时为了支持规模推广应用,降低业务线接入成本,保障优化效果,进行面向云上微服务,开展平台化优化服务系 ...
最新文章
- SQL Server 2017 AlwaysOn on Linux 配置和维护(18)
- MySQL错误1264和1366_Mysql错误1366 - Incorrect integer value解决方法
- Android 城市选择,热门城市,全部城市(美团、滴滴、淘宝)
- 解决[ERROR] [MY-013276] [Server] Failed to set datadir to ‘F:\Mysql\mysql-8.0.19-winx64\data\‘ (OS er
- VS2010中的错误代码号及其含义
- java利用随机数简单发牌,!!!!!!!java新手求助,请教一个数组下标越界异常的问题...
- 基于蒙特卡洛模型的排队问题求解
- 基于阿里云的基础架构设施保障(一)IAAS云计算
- 驱动大师服务器获取信息失败,360驱动大师、鲁大师,连测硬件信息都不准,我拿什么信你们...
- matlab残差的计算,请教MATLAB中拟合圆或者椭圆的残差如何计算
- 集成了谷歌翻译、百度翻译、有道翻译、和金山翻译的小助手软件
- 【WAF技巧拓展】————4、web应用防火墙逃逸技术(一)
- c java gs_Java基础-gs(垃圾回收)
- ThinkPad E450 进入dos界面快捷键 按F1
- 朋友圈一杠中间一个点_朋友圈看到这条线,大概率是被删了
- Android Animations动画使用详解
- FL Studio水果最新版2023安装图文详细教程
- 一次应用访问数据库 IP 配成外网 IP 引发的血案
- 最新开放的.Co域名注册向导
- linux 两个子进程,父进程创建两个子进程,在收到中断信号后杀死两个子进程
热门文章
- FBP中的滤波器设计
- 解决彩虹六号 2020 第二赛季更新后无法锁亚服
- android q微信打不开,微信打不开怎么回事?微信打不开怎么办?
- 电魂网络2018年营收达4.48亿元 同比下降10.19%
- 单例模式之懒汉与饿汉
- python循环嵌套三角形_python使用while、for及循环嵌套实现直角三角形及正、倒金字塔...
- qdialog 返回值_QDialog 窗口级别模态(续)
- 小程序wxParse不显示图片,图片链接错误不完整,解决方案!
- Android Studio执行出现“应用程序安装异常(-15)
- 百度字数统计代码研究