欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Android 之使用libjpeg压缩图片

程序员文章站 2022-07-03 15:01:26
一、环境Android Studio 4.0.1NDK R15cso包是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, q...

一、环境

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、引入头文件,并解决头文件中的报错,一般都是因为关联文件目录不对,头文件则从编译的源码中提取需要什么头文件

则引入对应的头文件

Android 之使用libjpeg压缩图片

 

 

 

 

 

 

 3、引入so文件,编写CMakeList.txt

Android 之使用libjpeg压缩图片

注意: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 <jni.h>
#include <string>
#include "com_xf_imageselector_utils_ImageUtil.h"
#include <android/bitmap.h>
#include <android/log.h>
#include <stdio.h>
#include <setjmp.h>
#include <math.h>
#include <stdint.h>
#include <time.h>
#include <malloc.h>
#include <stdlib.h>
//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之ndk