文档处理控制栏:
* [x] 选题收集:
* [ ] 初稿整理:
* [ ] 补充校对:
* [ ] 入库存档:
---
原文链接:[Android图片压缩的几种方案](https://juejin.im/entry/5a41c473f265da430f3258a7)
---
原文:http://blog.csdn.net/qq_30379689/article/details/78884167
## 效果演示
直接先给大家对比几种图片压缩的效果
![](https://user-gold-cdn.xitu.io/2017/12/26/16090e779a536711?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)
## 质量压缩
质量压缩:根据传递进去的质量大小,采用系统自带的压缩算法,将图片压缩成JPEG格式
~~~
/**
* 质量压缩
*
* @param bitmap
* @param quality
* @param file
*/
public static void compressQuality(Bitmap bitmap, int quality, File file) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos);
try {
FileOutputStream fos = new FileOutputStream(file);
fos.write(baos.toByteArray());
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
~~~
## 尺寸压缩
尺寸压缩:根据图片的缩放比例进行等比大小的缩小尺寸,从而达到压缩的效果
~~~
/**
* 尺寸压缩
*
* @param bitmap
* @param file
*/
public static void compressSize(Bitmap bitmap, File file) {
int ratio = 8;//尺寸压缩比例
Bitmap result = Bitmap.createBitmap(bitmap.getWidth() / ratio, bitmap.getHeight() / ratio, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);
Rect rect = new Rect(0, 0, bitmap.getWidth() / ratio, bitmap.getHeight() / ratio);
canvas.drawBitmap(bitmap, null, rect, null);
compressQuality(result, 100, file);
}
~~~
## 采样率压缩
采样率压缩:根据图片的采样率大小进行压缩
~~~
/**
* 采样率压缩
*
* @param filePath
* @param file
*/
public static void compressSample(String filePath, File file) {
int inSampleSize = 8;//采样率设置
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
options.inSampleSize = inSampleSize;
Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);
compressQuality(bitmap, 100, file);
}
~~~
## LibJpeg压缩
LibJpeg压缩:通过Ndk调用LibJpeg库进行压缩,保留原有的像素,清晰度高
### 编译LibJpeg
1、从Github上可以下载已经写好编译脚本的项目:https://github.com/Zelex/libjpeg-turbo-android ,并将其上传到Linux服务器的某个目录
![](data:image/svg+xml;utf8,)
2、授予整个目录权限
~~~
chmod 777 -R libjpeg-turbo-android-master
~~~
3、进入libjpeg目录,使用下面指令进行编译,前提是你的服务器已经搭建了ndk-build和配置了环境变量
~~~
ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./Android.mk APP_ABI=armeabi-v7a obj/local/armeabi-v7a/libjpeg.a LOCAL_ARM_MODE=arm LOCAL_ARM_NEON=true ARCH_ARM_HAVE_NEON=true
~~~
4、接着编译成功后,会在 obj/local 目录下生成我们需要的 libjpeg.a
![](https://user-gold-cdn.xitu.io/2017/12/26/16090e7799d328cb?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)
### 创建工程
1、创建一个新的项目,勾选包含C++,勾选C++11和C++的依赖库
![](https://user-gold-cdn.xitu.io/2017/12/26/16090e779a301188?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)
2、将生成的 libjpeg.a和头文件导入到我们的项目中
![](https://user-gold-cdn.xitu.io/2017/12/26/16090e77bf59d0ca?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)
3、配置gradle
~~~
android {
compileSdkVersion 25
buildToolsVersion "25.0.3"
defaultConfig {
applicationId "com.handsome.bitmapcompress"
minSdkVersion 16
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags "-std=c++11 -frtti -fexceptions"
//支持的CPU类型
abiFilters "armeabi", "armeabi-v7a"
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
} //修改Libs库的路径
sourceSets.main {
jniLibs.srcDirs = ['libs']
jni.srcDirs = []
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
~~~
4、配置CMake
~~~
cmake_minimum_required(VERSION 3.4.1)
include_directories(./libs/jpeg)
link_directories(./libs/${ANDROID_ABI})
find_library(log-lib log)
find_library(android-lib
android)
find_library(bitmap-lib
jnigraphics)
add_library( # Sets the name of the library.
native-lib # Sets the library as a shared library.
SHARED # Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp )
target_link_libraries( native-lib
${log-lib}
${android-lib}
${bitmap-lib}
jpeg )
~~~
5、声明权限
~~~
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
~~~
### 使用LibJpeg
1、启动选择文件的Intent
~~~
/**
* 选择文件
*/public void selectFile() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
startActivityForResult(new Intent(Intent.ACTION_GET_CONTENT).setType("image/*"), REQUEST_PICK_IMAGE);
} else {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
startActivityForResult(intent, REQUEST_KITKAT_PICK_IMAGE);
}
}
~~~
2、对返回的结果进行压缩
~~~
/**
* 返回结果
*
* @param requestCode
* @param resultCode
* @param data
*/@Overridepublic void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { switch (requestCode) { case REQUEST_PICK_IMAGE: if (data != null) {
Uri uri = data.getData();
compressImage(uri);
} break; case REQUEST_KITKAT_PICK_IMAGE: if (data != null) {
Uri uri = ensureUriPermission(this, data);
compressImage(uri);
} break;
}
}
}/**
* 压缩图片
* 注意:记得手动开启权限
*
* @param uri
*/public void compressImage(Uri uri) { try {
File saveFile = new File(getExternalCacheDir(), "NDK压缩.jpg");
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri); int code = CompressUtils.compressBitmap(bitmap, 20, saveFile.getAbsolutePath().getBytes(), true);
File saveFile1 = new File(getExternalCacheDir(), "质量压缩.jpg");
CompressUtils.compressQuality(bitmap, 20, saveFile1);
File saveFile2 = new File(getExternalCacheDir(), "尺寸压缩.jpg");
CompressUtils.compressSize(bitmap, saveFile2); //采样率比较特殊,需要传递文件的目录,这里采用直接指定目录的文件
File saveFile3 = new File(getExternalCacheDir(), "采样率压缩.jpg");
File LocalFile = new File("/storage/emulated/0/DCIM/Camera/IMG_20171216_171956.jpg"); if (LocalFile.exists()) {
CompressUtils.compressSample(LocalFile.getAbsolutePath(), saveFile3);
}
} catch (IOException e) {
e.printStackTrace();
}
}
~~~
3、加载本地库和声明LibJpeg压缩方法
~~~
public class CompressUtils { static {
System.loadLibrary("native-lib");
} public static native int compressBitmap(Bitmap bitmap, int quality, byte[] fileNameBytes, boolean optimize);
}
~~~
4、编写LibJpeg的本地文件
* 提取图片的ARGB通量的RGB通量
* 采用LibJpeg的API进行压缩
* 将数据写入到文件中
~~~
#include <jni.h>#include <string>#include <android/bitmap.h>#include <android/log.h>#include <setjmp.h>extern "C" { #include "jpeglib.h" #include "cdjpeg.h"}#define LOG_TAG "jni"#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)typedef uint8_t BYTE;typedef struct my_error_mgr *my_error_ptr;struct my_error_mgr { struct jpeg_error_mgr pub;
jmp_buf setjmp_buffer;
};
METHODDEF(void)
my_error_exit(j_common_ptr cinfo) {
my_error_ptr myerr = (my_error_ptr) cinfo->err;
(*cinfo->err->output_message)(cinfo);
LOGE("jpeg_message_table[%d]:%s", myerr->pub.msg_code,
myerr->pub.jpeg_message_table[myerr->pub.msg_code]);
longjmp(myerr->setjmp_buffer, 1);
} /**
* 采用Libjpeg压缩
* @param data
* @param w
* @param h
* @param quality
* @param outfilename
* @param optimize
* @return
*/int generateJPEG(BYTE *data, int w, int h, int quality, const char *outfilename, jboolean optimize) { //jpeg的结构体,保存的比如宽、高、位深、图片格式等信息
struct jpeg_compress_struct jcs; //当读完整个文件的时候就会回调my_error_exit
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); //打开输出文件
FILE* f = fopen(outfilename, "wb"); if (f == NULL) { return 0;
} //设置结构体的文件路径
jpeg_stdio_dest(&jcs, f);
jcs.image_width = w;//设置宽高
jcs.image_height = h; //设置哈夫曼编码,TRUE=arithmetic coding, FALSE=Huffman
if (optimize) {
jcs.arith_code = false;
} else {
jcs.arith_code = true;
} //颜色通道数量
int nComponent = 3;
jcs.input_components = nComponent; //设置结构体的颜色空间为RGB
jcs.in_color_space = JCS_RGB; //全部设置默认参数
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;
} extern "C"JNIEXPORT jint JNICALL
Java_com_handsome_bitmapcompress_CompressUtils_compressBitmap(JNIEnv *env, jclass type,
jobject bitmap,
jint quality,
jbyteArray fileNameBytes_,
jboolean optimize) { //获取Bitmap信息
AndroidBitmapInfo android_bitmap_info;
AndroidBitmap_getInfo(env, bitmap, &android_bitmap_info); //获取bitmap的 宽,高,format
int w = android_bitmap_info.width; int h = android_bitmap_info.height; int format = android_bitmap_info.format; if (format != ANDROID_BITMAP_FORMAT_RGBA_8888) { return -1;
} //存储ARGB所有像素点
BYTE *pixelsColor; //1、读取Bitmap所有像素信息
AndroidBitmap_lockPixels(env, bitmap, (void **) &pixelsColor); //2、解析每个像素,去除A通量,取出RGB通量
int i = 0, j = 0;
BYTE a, r, g, b; //存储RGB所有像素点
BYTE *data;
data = (BYTE *) malloc(w * h * 3); //存储RGB首地址
BYTE *tempData = data; int color; for (i = 0; i < h; ++i) { for (j = 0; j < w; ++j) { //将8位通道转成32位通道
color = *((int *) pixelsColor); //取值
a = ((color & 0xFF000000) >> 24);
r = ((color & 0x00FF0000) >> 16);
g = ((color & 0x0000FF00) >> 8);
b = ((color & 0x000000FF)); //赋值
*data = b;
*(data + 1) = g;
*(data + 2) = r; //指针往后移
data += 3;
pixelsColor += 4;
}
} //3、读取像素点完毕
AndroidBitmap_unlockPixels(env, bitmap); char *fileName = jstrinTostring(env, fileNameBytes_); //4、采用Libjpeg进行压缩
int resultCode = generateJPEG(tempData, w, h, quality, fileName, optimize); if (resultCode == 0) { return 0;
} return 1;
}
~~~
需要跑一下以上几种方案源码的同学,可以访问:https://github.com/AndroidHensen/BitmapCompress 获取。
---
参考文章
* [Android图片压缩的几种方案](https://juejin.im/entry/5a41c473f265da430f3258a7)
- 0-发现
- AndroidInterview-Q-A
- Android能让你少走弯路的干货整理
- LearningNotes
- temp
- temp11
- 部分地址
- 0-待办任务
- 待补充列表
- 0-未分类
- AndroidView事件分发与滑动冲突处理
- Spannable
- 事件分发机制详解
- 1-Java
- 1-Java-01基础
- 未归档
- 你应该知道的JDK知识
- 集合框架
- 1-Java-04合集
- Java之旅0
- Java之旅
- JAVA之旅01
- JAVA之旅02
- JAVA之旅03
- JAVA之旅04
- JAVA之旅05
- JAVA之旅06
- JAVA之旅07
- JAVA之旅08
- JAVA之旅09
- java之旅1
- JAVA之旅10
- JAVA之旅11
- JAVA之旅12
- JAVA之旅13
- JAVA之旅14
- JAVA之旅15
- JAVA之旅16
- JAVA之旅17
- JAVA之旅18
- JAVA之旅19
- java之旅2
- JAVA之旅20
- JAVA之旅21
- JAVA之旅22
- JAVA之旅23
- JAVA之旅24
- JAVA之旅25
- JAVA之旅26
- JAVA之旅27
- JAVA之旅28
- JAVA之旅29
- java之旅3
- JAVA之旅30
- JAVA之旅31
- JAVA之旅32
- JAVA之旅33
- JAVA之旅34
- JAVA之旅35
- 1-Java-05辨析
- HashMapArrayMap
- Java8新特性
- Java8接口默认方法
- 图解HashMap(1)
- 图解HashMap(2)
- 2-Android
- 2-Android-1-基础
- View绘制流程
- 事件分发
- AndroidView的事件分发机制和滑动冲突解决
- 自定义View基础
- 1-安卓自定义View基础-坐标系
- 2-安卓自定义View基础-角度弧度
- 3-安卓自定义View基础-颜色
- 自定义View进阶
- 1-安卓自定义View进阶-分类和流程
- 10-安卓自定义View进阶-Matrix详解
- 11-安卓自定义View进阶-MatrixCamera
- 12-安卓自定义View进阶-事件分发机制原理
- 13-安卓自定义View进阶-事件分发机制详解
- 14-安卓自定义View进阶-MotionEvent详解
- 15-安卓自定义View进阶-特殊形状控件事件处理方案
- 16-安卓自定义View进阶-多点触控详解
- 17-安卓自定义View进阶-手势检测GestureDetector
- 2-安卓自定义View进阶-绘制基本图形
- 3-安卓自定义View进阶-画布操作
- 4-安卓自定义View进阶-图片文字
- 5-安卓自定义View进阶-Path基本操作
- 6-安卓自定义View进阶-贝塞尔曲线
- 7-安卓自定义View进阶-Path完结篇伪
- 8-安卓自定义View进阶-Path玩出花样PathMeasure
- 9-安卓自定义View进阶-Matrix原理
- 通用类介绍
- Application
- 2-Android-2-使用
- 2-Android-02控件
- ViewGroup
- ConstraintLayout
- CoordinatorLayout
- 2-Android-03三方使用
- Dagger2
- Dagger2图文完全教程
- Dagger2最清晰的使用教程
- Dagger2让你爱不释手-终结篇
- Dagger2让你爱不释手-重点概念讲解、融合篇
- dagger2让你爱不释手:基础依赖注入框架篇
- 阅读笔记
- Glide
- Google推荐的图片加载库Glide:最新版使用指南(含新特性)
- rxjava
- 这可能是最好的RxJava2.x入门教程完结版
- 这可能是最好的RxJava2.x入门教程(一)
- 这可能是最好的RxJava2.x入门教程(三)
- 这可能是最好的RxJava2.x入门教程(二)
- 这可能是最好的RxJava2.x入门教程(五)
- 这可能是最好的RxJava2.x入门教程(四)
- 2-Android-3-优化
- 优化概况
- 各种优化
- Android端秒开优化
- apk大小优化
- 内存分析
- 混淆
- 2-Android-4-工具
- adb命令
- 一键分析Android的BugReport
- 版本控制
- git
- git章节简述
- 2-Android-5-源码
- HandlerThread 源码分析
- IntentService的使用和源码分析
- 2-Android-9-辨析
- LRU算法
- 什么是Bitmap
- 常见图片压缩方式
- 3-Kotlin
- Kotlin使用笔记1-草稿
- Kotlin使用笔记2
- kotlin特性草稿
- Kotlin草稿-Delegation
- Kotlin草稿-Field
- Kotlin草稿-object
- 4-JavaScript
- 5-Python
- 6-Other
- Git
- Gradle
- Android中ProGuard配置和总结
- gradle使用笔记
- Nexus私服搭建
- 编译提速最佳实践
- 7-设计模式与架构
- 组件化
- 组件化探索(OKR)
- 1-参考列表
- 2-1-组件化概述
- 2-2-gradle配置
- 2-3-代码编写
- 2-4-常见问题
- 2-9-值得一读
- 8-数据结构与算法
- 0临时文件
- 汉诺塔
- 8-数据-1数据结构
- HashMap
- HashMap、Hashtable、HashSet 和 ConcurrentHashMap 的比较
- 迟到一年HashMap解读
- 8-数据-2算法
- 1个就够了
- Java常用排序算法(必须掌握的8大排序算法)
- 常用排序算法总结(性能+代码)
- 必须知道的八大种排序算法(java实现)
- 9-职业
- 阅读
- 书单
- 面试
- 面试-01-java
- Java面试题全集骆昊(上)
- Java面试题全集骆昊(下)
- Java面试题全集骆昊(中)
- 面试-02-android
- 40道Android面试题
- 面试-03-开源源码
- Android图片加载框架最全解析(二),从源码的角度理解Glide的执行流程
- 面试-07-设计模式
- 面试-08-算法
- 面试-09-其他
- SUMMARY
- 版权说明
- temp111