android使用的图片压缩格式,Android 之使用libjpeg压缩图片
一、環境
Android Studio 4.0.1
NDK R15c
so包是2.0.6生成的 libturbojpeg.so
二、準備
1、編寫native方法并用javah命令生成頭文件
object ImageUtil {
private external fun compressBitmap(bitmap: Bitmap, quality: Int, fileName: String): Int
fun compressImage(bitmap: Bitmap, quality: Int, fileName: String) {
compressBitmap(bitmap, quality, fileName)
}
init {
System.loadLibrary("turbojpeg");
System.loadLibrary("compress");
}
fun decodeFile(filePath: String): Bitmap {
var finalWidth = 800
var opstions = BitmapFactory.Options()
//不加在圖片到內存,只拿寬高
opstions.inJustDecodeBounds = true
BitmapFactory.decodeFile(filePath, opstions)
var bitmapWidth = opstions.outWidth
var inSampleSize = 1
if (bitmapWidth > finalWidth) {
inSampleSize = bitmapWidth / finalWidth
}
opstions.inSampleSize = inSampleSize
opstions.inJustDecodeBounds = false
return BitmapFactory.decodeFile(filePath, opstions)
}
}
注意:如果javah提示 無法確定Bitmap的簽名,則指定對應的android.jar即可
F:\Android\workspace\ImageSelector\app\src\main\java>javah -classpath
F:\Android\android-sdk\platforms\android-28\android.jar; com.xf.imageselector.utils.Test
2、引入頭文件,并解決頭文件中的報錯,一般都是因為關聯文件目錄不對,頭文件則從編譯的源碼中提取需要什么頭文件
則引入對應的頭文件
3、引入so文件,編寫CMakeList.txt
注意:so一定要是對應架構下的,不然可能報錯找不到jpeg_**的方法錯誤,
例如undefined reference to 'jpeg_destroy_decompress'
4、編寫CMakeList文件,我的文件和app目錄平級,所以導入文件目錄可能有不一致
cmake_minimum_required(VERSION 3.4.1)
add_library(compress
SHARED
src/main/cpp/compress_image.cpp)
find_library(log-lib log)
add_library(libjpeg
SHARED
IMPORTED)
set_target_properties(libjpeg
PROPERTIES
IMPORTED_LOCATION
${CMAKE_CURRENT_SOURCE_DIR}/src/main/jniLibs/armeabi/libjpeg.so)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/jpeg)
target_link_libraries(compress
libjpeg
jnigraphics
${log-lib})
注意:如果你的目錄寫正確了,你按著ctrl鼠標可以點過去。一定要導入頭文件,不然可能報找不到jpeg方法
5、gradle文件配置
defaultConfig {
applicationId "com.xf.imageselector"
minSdkVersion 21
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags "-fexceptions", "-frtti"//-std=c++11
abiFilters "armeabi"
}
}
ndk {
abiFilters 'armeabi'
}
}
三、編寫文件
#include
#include
#include "com_xf_imageselector_utils_ImageUtil.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
//log打印
#define LOG_TAG "wxf"
#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 true 1
#define false 0
//把頭文件用c包裹起來,允許混合編譯,不然會報錯
extern "C" {
#include "jpeg/jpeglib.h"
#include "jpeg/jconfig.h"
#include "jpeg/jconfigint.h"
#include "jpeg/jerror.h"
#include "jpeg/jmorecfg.h"
}
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];
LOGI("jpeg_message_table[%d]:%s", myerr->pub.msg_code,
myerr->pub.jpeg_message_table[myerr->pub.msg_code]);
longjmp(myerr->setjmp_buffer, 1);
}
int
generateJPEG(BYTE *data, int w, int h, int quality, const char *outfileName, jboolean optimize) {
//結構體相當于java的類
struct jpeg_compress_struct jcs;
// 當讀完整個文件的時候回回調
struct my_error_mgr jem;
jcs.err = jpeg_std_error(&jem.pub);
jem.pub.error_exit = my_error_exit;
//setjmp是一個系統級函數,是一個回調
if (setjmp(jem.setjmp_buffer)) {
return 0;
}
//初始化jsc結構體
jpeg_create_compress(&jcs);
//打開輸出文件 wb可寫 rb可讀
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
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;
jpeg_set_defaults(&jcs);
// 是否采用哈夫曼
jcs.optimize_coding = optimize;
//設置質量
jpeg_set_quality(&jcs, quality, true);
// 開始壓縮
jpeg_start_compress(&jcs, TRUE);
JSAMPROW row_pointer[1];
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];
jpeg_write_scanlines(&jcs, row_pointer, 1);
}
// 壓縮結束
jpeg_finish_compress(&jcs);
// 銷毀回收內存
jpeg_destroy_compress(&jcs);
// 關閉文件
fclose(f);
return 1;
}
extern "C" jint Java_com_xf_imageselector_utils_ImageUtil_compressBitmap
(JNIEnv *env,
jobject thiz, jobject bitmap, jint quality, jstring fileNameStr) {
//解析RGB
//1.1獲取bitmap信息,w、h,format Android的Native要有了解
AndroidBitmapInfo info;
// java你調用完方法,往往返回的是對象,而c是參數
AndroidBitmap_getInfo(env, bitmap, &info);
// 從地址獲取值
int bitmap_height = info.height;
int bitmap_width = info.width;
int bitmap_format = info.format;
if (bitmap_format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
//argb
return -1;
}
LOGI("bitmap_height = %d,bitmap_width = %d,", bitmap_height, bitmap_width);
//1.2 把bitmap解析到數組中,數組保存的是rgb->YCbCr
//1.2.1 鎖定畫布
BYTE *pixel_color;
AndroidBitmap_lockPixels(env, bitmap, (void **) &pixel_color);
// 1.2.2解析數據,定義一些變量
BYTE *data;
BYTE r, g, b;
//申請一塊內存=寬*高*3
data = (BYTE *) malloc(bitmap_width * bitmap_height * 3);
// 數組指針指向的是數組首地址,因為這塊內存需要釋放,所以先保存一下
BYTE *tempData;
tempData = data;
//一個一個像素保存到data
int i = 0;
int j = 0;
int color;
for (int i = 0; i < bitmap_height; ++i) {
for (int j = 0; j < bitmap_width; ++j) {
//獲取二維數組的每一個像素信息的首地址
color = *((int *) pixel_color);
// 取出RGB
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下就是取下一個像素點
pixel_color += 4;
}
}
//1.2.3解鎖畫布
AndroidBitmap_unlockPixels(env, bitmap);
// 1.2.4 還差一個參數,jstring-》char*
char *file_name = (char *) env->GetStringUTFChars(fileNameStr, NULL);
LOGI("file_name = %s", file_name);
// 2、調用第三方提供好的方法,賦值
int result = generateJPEG(tempData, bitmap_width, bitmap_height, quality, file_name, true);
// 3、回收內存
free(tempData);
env->ReleaseStringUTFChars(fileNameStr, file_name);
// 釋放bitmap,調用bitmap的Recycler
//3.2獲取對象的class
jclass obj_clazz = env->GetObjectClass(bitmap);
// 3.3 通過class獲取方法的id
jmethodID method_id = env->GetMethodID(obj_clazz, "recycle", "()V");
//3.4調用方法釋放bitmap
env->CallVoidMethod(bitmap, method_id);
LOGI("result = %d", result);
// 4、返回結果
if (result == 0) {
return -1;
}
return 1;
}
注意:頭文件不需做任何改動,直接引入就好
四、測試,生成的so在build的cmake文件夾下可以找到
源圖片1.13M壓縮完只有300k左右,我的壓縮比是95,圖片也沒有失真
我的項目源碼我會放在資源中,名字turbolibjpeg
我嘗試用過 libturbojpeg.so,并引入了turbojpeg.h,但還是找不到jpeg的方法,所以之能引入libjpeg.so
本文地址:https://blog.csdn.net/wanxiaofan/article/details/107961108
總結
以上是生活随笔為你收集整理的android使用的图片压缩格式,Android 之使用libjpeg压缩图片的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android 多参数,玩转Androi
- 下一篇: android 数组指针异常,Andro