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方法
1 public class JpegUtils { 2 static { 3 System.loadLibrary("native-lib"); 4 } 5 6 public static native boolean compressBitmap(Bitmap bitmap, int width, int height, String fileName, int quality); 7 }
在新建的方法上直接生成c++方法。
把剛才jpeg庫的頭文件導入到cpp\include目錄下。我只保留了android下面的頭文件和其他的.h以及.c文件,其實這里面有無用的,但是具體不清楚,所以直接導入了。
jpeg壓縮的步驟
1、將Android的bitmap解碼並轉換為RGB數據
2、為JPEG對象分配空間並初始化
3、指定壓縮數據源
4、獲取文件信息
5、為壓縮設定參數,包括圖像大小,顏色空間
6、開始壓縮
7、壓縮完畢
8、釋放資源
在native-lib文件中進行代碼編寫
1 extern "C" 2 JNIEXPORT jboolean JNICALL 3 Java_com_nick_compress_JpegUtils_compressBitmap(JNIEnv *env, jclass type, jobject bitmap, 4 jint width, jint height, jstring fileName, 5 jint quality) { 6 7 AndroidBitmapInfo infoColor; 8 BYTE *pixelColor; 9 BYTE *data; 10 BYTE *tempData; 11 const char *filename = env->GetStringUTFChars(fileName, 0); 12 13 if ((AndroidBitmap_getInfo(env, bitmap, &infoColor)) < 0) { 14 LOGE("解析錯誤"); 15 return false; 16 } 17 18 if ((AndroidBitmap_lockPixels(env, bitmap, (void **) &pixelColor)) < 0) { 19 LOGE("加載失敗"); 20 return false; 21 } 22 23 BYTE r, g, b; 24 int color; 25 data = (BYTE *) malloc(width * height * 3); 26 tempData = data; 27 for (int i = 0; i < height; i++) { 28 for (int j = 0; j < width; j++) { 29 color = *((int *) pixelColor); 30 r = ((color & 0x00FF0000) >> 31 16);//與操作獲得rgb,參考java Color定義alpha color >>> 24 red (color >> 16) & 0xFF 32 g = ((color & 0x0000FF00) >> 8); 33 b = color & 0X000000FF; 34 35 *data = b; 36 *(data + 1) = g; 37 *(data + 2) = r; 38 data += 3; 39 pixelColor += 4; 40 } 41 } 42 43 AndroidBitmap_unlockPixels(env, bitmap); 44 int resultCode = generateJPEG(tempData, width, height, quality, filename, true); 45 46 free(tempData); 47 if (resultCode == 0) { 48 return false; 49 } 50 51 return true; 52 } 53 54 extern "C" 55 //圖片壓縮方法 56 int generateJPEG(BYTE *data, int w, int h, int quality, 57 const char *outfilename, jboolean optimize) { 58 int nComponent = 3; 59 60 struct jpeg_compress_struct jcs; 61 62 struct jpeg_error_mgr jem; 63 64 jcs.err = jpeg_std_error(&jem); 65 66 //為JPEG對象分配空間並初始化 67 jpeg_create_compress(&jcs); 68 //獲取文件信息 69 FILE *f = fopen(outfilename, "wb"); 70 if (f == NULL) { 71 return 0; 72 } 73 //指定壓縮數據源 74 jpeg_stdio_dest(&jcs, f); 75 jcs.image_width = w;//image_width->JDIMENSION->typedef unsigned int 76 jcs.image_height = h; 77 78 jcs.arith_code = false; 79 //input_components為1代表灰度圖,在等於3時代表彩色位圖圖像 80 jcs.input_components = nComponent; 81 if (nComponent == 1) 82 //in_color_space為JCS_GRAYSCALE表示灰度圖,在等於JCS_RGB時代表彩色位圖圖像 83 jcs.in_color_space = JCS_GRAYSCALE; 84 else 85 jcs.in_color_space = JCS_RGB; 86 87 jpeg_set_defaults(&jcs); 88 //optimize_coding為TRUE,將會使得壓縮圖像過程中基於圖像數據計算哈弗曼表,由於這個計算會顯著消耗空間和時間,默認值被設置為FALSE。 89 jcs.optimize_coding = optimize; 90 //為壓縮設定參數,包括圖像大小,顏色空間 91 jpeg_set_quality(&jcs, quality, true); 92 //開始壓縮 93 jpeg_start_compress(&jcs, TRUE); 94 95 JSAMPROW row_pointer[1];//JSAMPROW就是一個字符型指針 定義一個變量就等價於=========unsigned char *temp 96 int row_stride; 97 row_stride = jcs.image_width * nComponent; 98 while (jcs.next_scanline < jcs.image_height) { 99 row_pointer[0] = &data[jcs.next_scanline * row_stride]; 100 //寫入數據 http://www.cnblogs.com/darkknightzh/p/4973828.html 101 jpeg_write_scanlines(&jcs, row_pointer, 1); 102 } 103 104 //壓縮完畢 105 jpeg_finish_compress(&jcs); 106 //釋放資源 107 jpeg_destroy_compress(&jcs); 108 fclose(f); 109 110 return 1; 111 }
這段代碼比較多,但是這是很常用的jpeg庫的使用,網上解釋比較多,我這里也進行了較詳細的注釋,這里不過多的描述。
OK,代碼的編寫就到這里,點擊運行。——崩撒卡拉卡,果然沒能運行成功。顯示好多undifined reference,熟悉NDK的都知道我們需要在mk文件中去定義這些使用到的頭文件,但是我們項目是使用的Cmake工具進行編譯,所以需要在CMakelist.txt中去定義我們用到的庫及頭文件
CMakelist.txt
1 # Sets the minimum version of CMake required to build the native 2 # library. You should either keep the default value or only pass a 3 # value of 3.4.0 or lower. 4 5 cmake_minimum_required(VERSION 3.4.1) 6 7 # Creates and names a library, sets it as either STATIC 8 # or SHARED, and provides the relative paths to its source code. 9 # You can define multiple libraries, and CMake builds it for you. 10 # Gradle automatically packages shared libraries with your APK. 11 12 add_library( # Sets the name of the library. 13 native-lib 14 15 # Sets the library as a shared library. 16 SHARED 17 18 # Provides a relative path to your source file(s). 19 # Associated headers in the same location as their source 20 # file are automatically included. 21 src/main/cpp/native-lib.cpp ) 22 #include 這個目錄下所有的文件 23 include_directories(src/main/cpp/include) 24 #外部導入jpeg這個庫 25 add_library(jpeg SHARED IMPORTED ) 26 #這句話是jpeg對應的so文件,so文件是放到ibs這個文件夾中(相對與cpp這個文件的位置) 27 set_target_properties(jpeg PROPERTIES IMPORTED_LOCATION ../../../../libs/armeabi/libjpeg.so) 28 29 # Searches for a specified prebuilt library and stores the path as a 30 # variable. Because system libraries are included in the search path by 31 # default, you only need to specify the name of the public NDK library 32 # you want to add. CMake verifies that the library exists before 33 # completing its build. 34 find_library( # Sets the name of the path variable. 35 log-lib 36 37 # Specifies the name of the NDK library that 38 # you want CMake to locate. 39 log ) 40 41 # Specifies libraries CMake should link to your target library. You 42 # can link multiple libraries, such as libraries you define in the 43 # build script, prebuilt third-party libraries, or system libraries. 44 45 target_link_libraries( # Specifies the target library. 46 native-lib 47 48 # Links the target library to the log library 49 # included in the NDK. 50 jpeg 51 #jnigraphics這個是android下面的bitmap.h對應的庫 52 jnigraphics 53 ${log-lib})
用AS去開發NDK最難的地方並不是什么代碼,而是這個CMakelist文件。媽蛋想想我網上找了n久,真的資料太少了。NND!
有了上面的代碼就可以成功的運行了,我把代碼放到了Github(https://github.com/mcksuu/jpeg-android)上,需要的可以下下來看看。
5.最終效果
原圖和詳情:
壓縮后的圖片和詳情: