Android 调用jepg库进行图片压缩,保持图片不失真
1. 浅谈为什么Android和iOS图片质量差距那么大?
首先来说,作为一个安卓狗,机器当然用的是安卓的手机。现在的安卓手机大多数都会以高清拍照,动不动就几千万柔光相机来吸引各种买家。买来后,拍照发现,哇塞——一张图片好几M呢,但是还是不如iOS的感觉,iOS的图片也就1M左右吧。为什么会有这么大的差距呢?这要从安卓的设计初衷来说起,当时谷歌开发Android的时候,考虑了大部分手机的配置并没有那么高,所以对图片处理是使用的Skia这个库。当然这个库的底层还是是用的jpeg对图片进行压缩处理。但是为了能够适配低端的手机(这里的低端是指以前的硬件配置不高的手机),所以Skia在进行图片处理并没有去使用压缩图像过程中基于图像数据计算哈弗曼表(关于图片压缩中的哈弗曼表,请自行查阅相关资料),可以参考[这里](http://www.cnblogs.com/MaxIE/p/3951294.html)。这里面详细解释为何Google没有使用高性能的压缩,简单来说就是考虑了当时的手机硬件,将一个压缩参数optimize_coding设置为了false,使得硬件较低的手机能够很好的处理图片。
2. NDK环境以及Cmake配置(篇幅有限这里不做过多的描述)
添加环境变量
将配置的环境变量添加到系统环境变量中。把%NDK_HOME%;添加到Path中。
3. jpeg库的下载及编译.so文件
下载libjpeg库源码,git clone地址
git clone git://git.linaro.org/people/tomgall/libjpeg-turbo/libjpeg-turbo.git -b linaro-android
将clone下来的源码目录改为jni(即源目录libjpeg-turbo改为jni),通过ndk命令进行编译(需要配好ndk环境变量,命令行进入修改好的jni目录输入命令即可):
ndk-build APP_ABI=armeabi-v7a,armeabi
在当前目录下生成libs和obj文件夹
4. 新建一个Android项目
新建一个Android项目,并勾选c++support。
如果环境配置好的话,AS会自动生成一个包含NDK的项目,里面实现了hello world。目录结构如下图:
新建一个类,JpegUtils,声明native方法
public class JpegUtils {
static {
System.loadLibrary("native-lib");
} public static native boolean compressBitmap(Bitmap bitmap, int width, int height, String fileName, int quality);
}
在新建的方法上直接生成c++方法。
把刚才jpeg库的头文件导入到cpp\include目录下。我只保留了android下面的头文件和其他的.h以及.c文件,其实这里面有无用的,但是具体不清楚,所以直接导入了。
jpeg压缩的步骤
1、将Android的bitmap解码并转换为RGB数据
2、为JPEG对象分配空间并初始化
3、指定压缩数据源
4、获取文件信息
5、为压缩设定参数,包括图像大小,颜色空间
6、开始压缩
7、压缩完毕
8、释放资源
在native-lib文件中进行代码编写
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_nick_compress_JpegUtils_compressBitmap(JNIEnv *env, jclass type, jobject bitmap,
jint width, jint height, jstring fileName,
jint quality) { AndroidBitmapInfo infoColor;
BYTE *pixelColor;
BYTE *data;
BYTE *tempData;
const char *filename = env->GetStringUTFChars(fileName, 0); if ((AndroidBitmap_getInfo(env, bitmap, &infoColor)) < 0) {
LOGE("解析错误");
return false;
} if ((AndroidBitmap_lockPixels(env, bitmap, (void **) &pixelColor)) < 0) {
LOGE("加载失败");
return false;
} BYTE r, g, b;
int color;
data = (BYTE *) malloc(width * height * 3);
tempData = data;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
color = *((int *) pixelColor);
r = ((color & 0x00FF0000) >>
16);//与操作获得rgb,参考java Color定义alpha color >>> 24 red (color >> 16) & 0xFF
g = ((color & 0x0000FF00) >> 8);
b = color & 0X000000FF; *data = b;
*(data + 1) = g;
*(data + 2) = r;
data += 3;
pixelColor += 4;
}
} AndroidBitmap_unlockPixels(env, bitmap);
int resultCode = generateJPEG(tempData, width, height, quality, filename, true); free(tempData);
if (resultCode == 0) {
return false;
} return true;
} extern "C"
//图片压缩方法
int generateJPEG(BYTE *data, int w, int h, int quality,
const char *outfilename, jboolean optimize) {
int nComponent = 3; struct jpeg_compress_struct jcs; struct jpeg_error_mgr jem; jcs.err = jpeg_std_error(&jem); //为JPEG对象分配空间并初始化
jpeg_create_compress(&jcs);
//获取文件信息
FILE *f = fopen(outfilename, "wb");
if (f == NULL) {
return 0;
}
//指定压缩数据源
jpeg_stdio_dest(&jcs, f);
jcs.image_width = w;//image_width->JDIMENSION->typedef unsigned int
jcs.image_height = h; jcs.arith_code = false;
//input_components为1代表灰度图,在等于3时代表彩色位图图像
jcs.input_components = nComponent;
if (nComponent == 1)
//in_color_space为JCS_GRAYSCALE表示灰度图,在等于JCS_RGB时代表彩色位图图像
jcs.in_color_space = JCS_GRAYSCALE;
else
jcs.in_color_space = JCS_RGB; jpeg_set_defaults(&jcs);
//optimize_coding为TRUE,将会使得压缩图像过程中基于图像数据计算哈弗曼表,由于这个计算会显著消耗空间和时间,默认值被设置为FALSE。
jcs.optimize_coding = optimize;
//为压缩设定参数,包括图像大小,颜色空间
jpeg_set_quality(&jcs, quality, true);
//开始压缩
jpeg_start_compress(&jcs, TRUE); JSAMPROW row_pointer[1];//JSAMPROW就是一个字符型指针 定义一个变量就等价于=========unsigned char *temp
int row_stride;
row_stride = jcs.image_width * nComponent;
while (jcs.next_scanline < jcs.image_height) {
row_pointer[0] = &data[jcs.next_scanline * row_stride];
//写入数据 http://www.cnblogs.com/darkknightzh/p/4973828.html
jpeg_write_scanlines(&jcs, row_pointer, 1);
} //压缩完毕
jpeg_finish_compress(&jcs);
//释放资源
jpeg_destroy_compress(&jcs);
fclose(f); return 1;
}
这段代码比较多,但是这是很常用的jpeg库的使用,网上解释比较多,我这里也进行了较详细的注释,这里不过多的描述。
OK,代码的编写就到这里,点击运行。——崩撒卡拉卡,果然没能运行成功。显示好多undifined reference,熟悉NDK的都知道我们需要在mk文件中去定义这些使用到的头文件,但是我们项目是使用的Cmake工具进行编译,所以需要在CMakelist.txt中去定义我们用到的库及头文件
CMakelist.txt
# Sets the minimum version of CMake required to build the native
# library. You should either keep the default value or only pass a
# value of 3.4.0 or lower. cmake_minimum_required(VERSION 3.4.1) # Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds it for you.
# Gradle automatically packages shared libraries with your APK. 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).
# Associated headers in the same location as their source
# file are automatically included.
src/main/cpp/native-lib.cpp )
#include 这个目录下所有的文件
include_directories(src/main/cpp/include)
#外部导入jpeg这个库
add_library(jpeg SHARED IMPORTED )
#这句话是jpeg对应的so文件,so文件是放到ibs这个文件夹中(相对与cpp这个文件的位置)
set_target_properties(jpeg PROPERTIES IMPORTED_LOCATION ../../../../libs/armeabi/libjpeg.so) # Searches for a specified prebuilt library and stores the path as a
# variable. Because system libraries are included in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib # Specifies the name of the NDK library that
# you want CMake to locate.
log ) # Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in the
# build script, prebuilt third-party libraries, or system libraries. target_link_libraries( # Specifies the target library.
native-lib # Links the target library to the log library
# included in the NDK.
jpeg
#jnigraphics这个是android下面的bitmap.h对应的库
jnigraphics
${log-lib})
用AS去开发NDK最难的地方并不是什么代码,而是这个CMakelist文件。妈蛋想想我网上找了n久,真的资料太少了。NND!
有了上面的代码就可以成功的运行了,我把代码放到了Github(https://github.com/mcksuu/jpeg-android)上,需要的可以下下来看看。
5.最终效果
原图和详情:
压缩后的图片和详情:
Android 调用jepg库进行图片压缩,保持图片不失真的更多相关文章
- [置顶] android调用第三方库——第四篇——调用多个第三方库
0:前言: 在前面三篇中我们介绍了android调用第三方库的形式,在这一篇中我们介绍调用多个第三方库的Android.mk的写法,由于其他三篇介绍的很详细,这里只给出Android.mk的内容. [ ...
- android调用第三方库——第二篇——编写库android程序直接调用第三方库libhello.so (转载)
转自:http://blog.csdn.net/jiuyueguang/article/details/9449737 版权声明:本文为博主原创文章,未经博主允许不得转载. 0:前言 1:本文主要作为 ...
- android调用第三方库——第一篇 (转载)
转自:http://blog.csdn.net/jiuyueguang/article/details/9447245 版权声明:本文为博主原创文章,未经博主允许不得转载. 0:前言: 这两天一直在研 ...
- 移动端 H5 拍照 从手机选择图片,移动端预览,图片压缩,图片预览,再上传服务器
前言:最近公司的项目在做全网营销,要做非微信浏览器的wap 站 的改版,其中涉及到的一点技术就是采用H5 选择手机相册中的图片,或者拍照,再将获取的图片进行压缩之后上传. 这个功能模块主要有这5点比较 ...
- java多图片上传--前端实现预览--图片压缩 、图片缩放,区域裁剪,水印,旋转,保持比例。
java多图片上传--前端实现预览 前端代码: https://pan.baidu.com/s/1cqKbmjBSXOhFX4HR1XGkyQ 解压后: java后台: <!--文件上传--&g ...
- Android 简单介绍图片压缩和图片内存缓存
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9316683 本篇文章主要内容来自于Android Doc,我翻译之后又做了些加工, ...
- 使用python调用zxing库生成二维码图片
(1) 安装Jpype 用python调用jar包须要安装jpype扩展,在Ubuntu上能够直接使用apt-get安装jpype扩展 $ sudo apt-get install pytho ...
- js 前端图片压缩+ios图片角度旋转
step1:读取选择的图片,并转为base64: function ImgToBase64 (e, fn) { // 图片方向角 //fn为传入的方法函数,在图片操作完成之后执行 var Orient ...
- Java实现图片压缩代码,图片大小转换
在很多项目中我们会把上传的图片做处理,比较图片上传过多对服务器的容量和带宽有很多的浪费,如果不是必须的高清图片,我们可以通过代码来做压缩.在我的项目中我们压缩图片的目的是让web页面打开的速度很快,并 ...
随机推荐
- Cookie 简单使用记录浏览记录
ItemsDAO.java package dao; import java.util.* ; import java.sql.* ; import util.DBHelper; import ent ...
- iOS 之 时间格式与字符串转换
这个知识点涉及到三个类:NSDate.NSString,另外是一个最重要的类NSDateFormatter.它起到格式转换的作用,至于方法查看头文件就好了.时间格式注意下:yyyyMMddHHmmss
- Python模块学习:threading 多线程控制和处理
Reference:http://python.jobbole.com/81546/ threading.Thread Thread 是threading模块中最重要的类之一,可以使用它来创建线程.有 ...
- android模拟器网络设置(局域网)
Android模拟器如何设置DNS访问局域网内网站 我们需要用到android-sdk开发包中adb shell指令 见下图
- div垂直居中(js)
window.onload = function(){ var xx = document.documentElement.clientHeight; // 470为要垂直居中的div的高度 he = ...
- 样式(Style)和主题(Theme)资源——样式资源
样式和主题资源都是用于对Android应用进行“美化”的,只要充分利用Android应用的样式和主题资源,开发者可以开发出各种风格的Android应用. 样式资源: 如果我们经常需要对 ...
- YII 1.0 分页类
在控制器中 方法1 $criteria = new CDbCriteria();//AR的另一种写法 $model = Article::model(); $total = $model->co ...
- Delphi 常用函数记录
//判断是否是数字 function IsNumeric(sDestStr: string): Boolean; //简写多余汉字 function SimplifyWord(sWord: strin ...
- word中利用宏替换标点标点全角与半角
Alt+F11,然后插入-模块: 复制下面代码到编辑窗口: Sub 半角标点符号转换为全角标点符号() '中英互译文档中将中文段落中的英文标点符号替换为中文标点符号 Dim i As Paragrap ...
- 添加redo日志组和添加日志组多元化
查看redo日志组的状态和日志的位置. SQL> 没有被使用,所以切几次日志,组合4已生效. SQL> select * from v$log; GROUP# THREAD# SEQ ...