1. 程式人生 > >Android圖片壓縮,自己編譯libjpeg

Android圖片壓縮,自己編譯libjpeg

之前的部落格提到過關於圖片壓縮的方法: Android 圖片壓縮,Bitmap優化


基於此so庫寫的一個圖片壓縮框架:https://github.com/JavaNoober/Light


Android原生的壓縮方法,不在乎兩種:通過設定simpleSize根據圖片尺寸壓縮圖片;通過Bitmap.compress方法通過壓縮圖片質量,去壓縮。但是我們當我們對圖片質量和圖片檔案大小同時存在要求時,我們發現無論怎麼去設定引數,我們所做的效果總是不能盡如人意,而且同樣大小的圖片,效果卻總是比iOS的效果差很多。

其實歸根到底的是iOS和Android的壓縮演算法存在一些差異。

Android所用的是skia的壓縮演算法,它在google的很多地方,比如chrome、Android等都有使用,而Bitmap的壓縮演算法就是通過這個實現。而Skia是libjpeg進行了封裝,google在實現skia時對其中一個地方進行了修改:通過哈夫曼演算法來進行圖片壓縮,但是採用這個演算法的時候,機器可能會出現效能問題,於是並沒有調動這個方法。

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.
但是,這個問題在十年前或許存在,但是現在的機器已經完全可以解決效能問題,而google卻忘記了更正當年的程式碼。


接下來,我們就自己去編譯生成libjpeg的動態庫,然後呼叫。

github中android libjpeg的原始檔地址:https://github.com/libjpeg-turbo/libjpeg-turbo

這個我們需要自己去編譯,但是已經有人幫我們編譯好了,壓縮演算法也已經實現,因此,我們去下載然後編譯即可:https://github.com/bither/bither-android-lib


首先將上面下載好的已經編譯好的libjpeg放到jni目錄下,將下圖內容都放到jni目錄中:


安裝好ndk以後,直接輸入ndk-build即可。

接下來就會編譯生成arm下的動態庫,使用的時候必須在專案中新建一個包net.bither.util,然後加入下面這個類方法,也就是使用了libjpeg開啟哈夫曼演算法的壓縮演算法:

/*
 * Copyright 2014 http://Bither.net
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.bither.util;

import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.util.Log;

public class NativeUtil {
	private static int DEFAULT_QUALITY = 95;

	public static void compressBitmap(Bitmap bit, String fileName,
			boolean optimize) {
		compressBitmap(bit, DEFAULT_QUALITY, fileName, optimize);

	}

	public static void compressBitmap(Bitmap bit, int quality, String fileName,
			boolean optimize) {
		Log.d("native", "compress of native");

		// if (bit.getConfig() != Config.ARGB_8888) {
		Bitmap result = null;

		result = Bitmap.createBitmap(bit.getWidth() / 3, bit.getHeight() / 3,
				Config.ARGB_8888);// 縮小3倍
		Canvas canvas = new Canvas(result);
		Rect rect = new Rect(0, 0, bit.getWidth(), bit.getHeight());// original
		rect = new Rect(0, 0, bit.getWidth() / 3, bit.getHeight() / 3);// 縮小3倍
		canvas.drawBitmap(bit, null, rect, null);
		saveBitmap(result, quality, fileName, optimize);
		result.recycle();
		// } else {
		// saveBitmap(bit, quality, fileName, optimize);
		// }

	}

	private static void saveBitmap(Bitmap bit, int quality, String fileName,
			boolean optimize) {

		compressBitmap(bit, bit.getWidth(), bit.getHeight(), quality,
				fileName.getBytes(), optimize);

	}

	private static native String compressBitmap(Bitmap bit, int w, int h,
			int quality, byte[] fileNameBytes, boolean optimize);

	static {
		System.loadLibrary("jpegbither");
		System.loadLibrary("bitherjni");

	}

}

注意包名和方法名都是不能變的,因為在編譯的時候已經被確定。

如果我們想要去修改方法名放入自己的專案中怎麼辦。那我們就需要去修改一下bitherlibjni.c這個檔案。

例如我想把這個方法放在com.example.test中的ImageUtils中,

我們只需要把c檔案中的

jstring Java_net_bither_util_NativeUtil_compressBitmap(JNIEnv* env,
      jobject thiz, jobject bitmapcolor, int w, int h, int quality,
      jbyteArray fileNameStr, jboolean optimize) {
修改為

jstring Java_com_example_test_ImageUtils_compressBitmap(JNIEnv* env,
      jobject thiz, jobject bitmapcolor, int w, int h, int quality,
      jbyteArray fileNameStr, jboolean optimize) {
這個對會ndk開發的同學應該都知道,接下來我們重新執行ndk-build就可以重新替換so檔案然後呼叫我們自己的libjpeg了。

但是,目前libjpeg是很多年前的了。github上這個庫只支援arm架構的cpu,如果我們想用這個庫的話,只能通過在載入so檔案的時候對其進行trycatch處理,來防止x86等其他cpu架構的機器載入so檔案報錯。


壓縮效果對比:

原圖:



bitmap壓縮:




libjpeg壓縮:




github下載地址:https://github.com/xiaoqiAndroid/LibJpegCompress/tree/master