JNI實現圖片壓縮

原文連接:blog.csdn.net/u014011112/…html

項目連接:github.com/zengfw/Effe…android

直接使用項目或直接複製libs中的so庫到項目中便可(當前只構建了armeabi),須要其餘ABI可檢下項目另外使用CMake構建便可。git

結果預覽:github

效果圖.png

jni_278KB.png

jni_278KB.png 算法

quality_484KB.png

quality_484KB.png bash

sample_199KB.png

sample_199KB.png ide

size_238KB.png

size_238KB.png 測試

原圖大小5.99M~~ 咱們把全部通過壓縮的圖片放到同等大小的狀況後,很明顯,採樣壓縮跟尺寸壓縮都不是咱們想要的結果,而質量壓縮跟JNI壓縮我設置的質量壓縮值都是30,JNI壓縮出來只有278KB,直接質量壓縮出來的有484KB,綜合以後,JNI纔是綜合最優的方式,固然,若是隻是頭像,咱們設置能夠把配置值設置得更小,圖片就更小。gradle

爲何iPhone手機圖片的質量比Android的好?

首先了解兩個圖像處理庫:libjpegSkiaui

Skia:圖像處理引擎,Google在Android系統上就是採用Skia,它是基於libjpeg的二次封裝,Google在不少其它產品也使用了這個庫,好比Chorme,Firefox等等。

libjpeg:早期的圖像處理引擎,用於PC端。

官方文檔能夠看到libjpeg.doc這樣一段話:

boolean optimize_coding
        TRUE causes the compressor to compute optimal Huffman coding tables
        for the image.  This requires an extra pass over the data and
        therefore costs a good deal of space and time.  The default is
        FALSE, which tells the compressor to use the supplied or default
        Huffman tables.  In most cases optimal tables save only a few percent
        of file size compared to the default tables.  Note that when this is
        TRUE, you need not supply Huffman tables at all, and any you do
        supply will be overwritten.
複製代碼

boolean optimize_coding:

  • 參數爲TRUE時,圖片壓縮算法使用最優的哈夫曼編碼表,它須要額外傳遞數據,所以會耗費CPU運算時間,以及開闢不少臨時內存空間。
  • 參數爲FALSE時,使用默認的哈夫曼編碼表。在大多數狀況,使用最優哈夫曼編碼表相比默認哈夫曼編碼表,能節省圖像文件很大比例的大小。

爲何使用最優哈夫曼編碼表能夠節省圖像文件很大的比例大小呢?

能夠先了解下什麼是哈夫曼樹和哈夫曼編碼

看完博客以後,能夠知道最優哈夫曼編碼實際上是使用了可變長編碼方式,而默認的哈夫曼編碼使用了定長編碼方式,所以須要更多的存儲空間,呈現出來的手機圖片天然會大很大。

libjpeg把optimize_coding參數默認設置爲FALSE是由於10多年前的Android手機CPU跟內存都很是吃緊,因此當年沒有設置爲TRUE。現在的手機CPU跟內存都「起飛了」,Goolge的Skia圖像處理引擎卻仍是使用optimize_coding的默認值FALSE。咱們沒法修改系統Skia的這個參數值,因此只能默默忍受size很大的圖像文件。

通過大量圖像壓縮測試結果,獲得兩個結論:

1.圖片壓縮到相同的質量,FALSE所產出的圖像文件大小是TRUE的5-10倍。 2.圖片壓縮到相同的質量,Android所產出的圖像文件大小比iOS也是大5-10倍。

因此,經過使用libjpeg編譯本身的native library修改optimize_coding參數的值,達圖像質量相同,所產出的圖像卻能節省5-10倍空間大小的效果。

實現的步驟:

1.構建libjpeg的so庫 到官方下載對應本身電腦系統類型的壓縮包,建立Android項目導入壓縮包裏頭的xx.h、xx.c文件構建so庫。bither/bither-android-lib已經作了這個工做,所以咱們只需直接拿他的libjpegbither.so便可。

2.導入libjpeg的聲明頭文件,由於步驟1的libjpegbither.so是對這些頭文件的實現,所以須要導入這些頭文件。

3.建立CMake腳本

cmake_minimum_required(VERSION 3.4.1)

add_library( effective-bitmap
             SHARED
             src/main/cpp/effective-bitmap.c )


include_directories( src/main/cpp/jpeg/
                     )

add_library(jpegbither SHARED IMPORTED)
set_target_properties(jpegbither
  PROPERTIES IMPORTED_LOCATION
  ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libjpegbither.so)


find_library( log-lib
              log )

find_library( jnigraphics-lib jnigraphics )

target_link_libraries( effective-bitmap
                       jpegbither
                       ${log-lib}
                       ${jnigraphics-lib})
複製代碼

4.配置gradle關聯CMakeLists.txt構建腳本,以及指定產出的ABI的類型

android {
    // ...
    defaultConfig {
        // ...
        externalNativeBuild {
            cmake {
                cppFlags ""
            }
        }

        ndk {
            abiFilters 'armeabi'
        }
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}
複製代碼

5.核心C/C++代碼

int generateJPEG(BYTE* data, int w, int h, int quality,
                 const char* outfilename, jboolean optimize) {
    int nComponent = 3;
    // jpeg的結構體,保存的好比寬、高、位深、圖片格式等信息
    struct jpeg_compress_struct jcs;

    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;
    }
    jpeg_create_compress(&jcs);
    // 打開輸出文件 wb:可寫byte
    FILE* f = fopen(outfilename, "wb");
    if (f == NULL) {
        return 0;
    }
    // 設置結構體的文件路徑
    jpeg_stdio_dest(&jcs, f);
    jcs.image_width = w;
    jcs.image_height = h;

    // 設置哈夫曼編碼
    jcs.arith_code = false;
    jcs.input_components = nComponent;
    if (nComponent == 1)
        jcs.in_color_space = JCS_GRAYSCALE;
    else
        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;
}
複製代碼

6.構建so庫

源碼:github.com/zengfw/Effe…

參考連接: Why the image quality of iPhone is much better than Android?

相關文章
相關標籤/搜索