【Android 内存优化】Android 工程中使用 libjpeg-turbo 压缩图片 ( 初始化压缩对象 | 打开文件 | 设置压缩参数 | 写入压缩图像数据 | 完成压缩 | 释放资源 )
文章目錄
- 一、使用 libjpeg-turbo 壓縮圖片流程
- 二、初始化 JPEG 壓縮對象
- 三、打開文件
- 四、設置壓縮參數
- 五、開始壓縮
- 六、循環寫入壓縮數據
- 七、完成圖片壓縮及收尾
- 八、libjpeg-turbo 圖片壓縮案例 ( 官方示例 )
- 九、libjpeg-turbo 壓縮圖片代碼示例
上一篇博客 【Android 內存優化】Android 工程中使用 libjpeg-turbo 壓縮圖片 ( JNI 傳遞 Bitmap | 獲取位圖信息 | 獲取圖像數據 | 圖像數據過濾 | 釋放資源 ) 介紹了從 Java 層傳入 Bitmap 對象到 JNI 層 , JNI 層獲取到了圖像對應的 RGB 像素數據 , 本篇博客中將獲取的圖像數據進行壓縮 , 存儲到 JPEG 格式圖片中 ;
一、使用 libjpeg-turbo 壓縮圖片流程
使用 libjpeg-turbo 壓縮圖片流程 :
① 初始化壓縮對象 : 初始化 JPEG 圖片壓縮對象 ;
② 打開文件 : 使用 Linux C API 打開壓縮圖片寫出文件 ;
③ 設置壓縮參數 : 設置圖片壓縮參數 , 如圖片寬高 , 像素格式 , 數據格式 , 質量等 ;
④ 開始壓縮 : 啟動壓縮 ;
⑤ 寫入壓縮數據 : 圖像數據逐行輸入 , 并壓縮 ;
⑥ 壓縮完畢 : 壓縮完畢后調用對應方法 ;
⑦ 釋放資源 : 文件資源 , 及壓縮相關的內存資源 , 需要釋放掉 ;
二、初始化 JPEG 壓縮對象
1. 初始化 JPEG 壓縮對象 :
① JPEG 壓縮對象概念 : jpeg_compress_struct 結構體和與其關聯的工作數據 , 該對象中存儲了 JPEG 壓縮參數 , 還包含了指向工作空間的指針 , JPEG 庫會在需要時分配該指針;
② 壓縮對象個數 : 該結構體可能會存在多個 , 每個結構體對象都表示了一個壓縮或解壓縮的工作;
2. 錯誤處理機制 :
① 錯誤處理程序 : jpeg_error_mgr 結構體表示錯誤處理程序 , 將其單獨定義成一個結構體 , 是因為應用經常需要提供一個專門的錯誤處理程序;
② 處理處理機制 : 在這里我們采用最簡單的方法 , 使用標準的錯誤處理程序 , 如果 壓縮失敗 , 在 stderr 上打印失敗信息, 并調用 exit() 退出程序 ;
③ 結構體生命周期 : 該結構體的生命周期必須與 jpeg_compress_struct 結構體的生命周期保持一致 , 以免產生野指針問題 ;
④ 錯誤處理設置時間 : 在所有操作之前 , 設置錯誤處理程序 , 為了防止 JPEG 壓縮對象初始化時出錯, 越早設置錯誤處理程序越好 , 在內存不足時, 創建 jpeg_compress_struct 可能會失敗 ;
2. 代碼示例 :
/* 該對象中存儲了 JPEG 壓縮參數, 還包含了指向工作空間的指針, JPEG 庫會在需要時分配該指針;* 該結構體可能會存在多個, 每個結構體對象都表示了一個壓縮或解壓縮的工作;* JPEG 對象 : jpeg_compress_struct 結構體和與其關聯的工作數據*/struct jpeg_compress_struct cinfo;/* 錯誤處理程序 : jpeg_error_mgr 結構體表示錯誤處理程序,* 將其單獨定義成一個結構體, 是因為應用經常需要提供一個專門的錯誤處理程序;* 處理處理機制 : 在這里我們采用最簡單的方法, 使用標準的錯誤處理程序,* 如果壓縮失敗, 在 stderr 上打印失敗信息, 并調用 exit() 退出程序 ;* 結構體聲明周期 : 該結構體的生命周期必須與 jpeg_compress_struct 結構體的生命周期保持一致,* 以免產生野指針問題 ;*/struct jpeg_error_mgr jerr;/* 為了防止 JPEG 壓縮對象初始化時出錯, 這里首先設置錯誤處理* 在內存不足時, 創建 jpeg_compress_struct 可能會失敗*/cinfo.err = jpeg_std_error(&jerr);// 初始化 JPEG 壓縮對象jpeg_create_compress(&cinfo);三、打開文件
1. 打開文件 : 使用 Linux C 中的文件操作 , 調用 fopen 函數打開文件 , 傳入兩個參數 , 文件路徑名稱 , 和 打開模式 , 打開模式中 “wb” , w 代表寫出數據 , b 代表二進制數據 , 該模式的函數以是寫出二進制數據 ;
2. 為 JPEG 壓縮對象設置文件輸出 : 調用 jpeg_stdio_dest 函數 , 為 JPEG 對象設置輸出文件 ; 調用該函數的調用者需要負責文件打開 , 和文件關閉操作 ;
EXTERN(void) jpeg_stdio_dest(j_compress_ptr cinfo, FILE *outfile);3. 代碼示例 :
FILE *outfile;if ((outfile = fopen(filename, "wb")) == NULL) {fprintf(stderr, "can't open %s\n", filename);exit(1);}// 設置文件輸出jpeg_stdio_dest(&cinfo, outfile);四、設置壓縮參數
1 . 設置默認參數 :
- 圖片寬度 : cinfo.image_width ; 像素寬度 ;
- 圖片高度 : cinfo.image_height ; 像素高度 ;
- 像素組件 : cinfo.input_components ; 單個像素 BGR 3個組件 ;
- 顏色空間 : cinfo.in_color_space ; 輸入圖像的顏色空間 ;
上述四個參數設置完畢后 , 調用 jpeg_set_defaults 方法 , 設置默認參數 ;
2 . 設置非默認參數 :
- 哈夫曼編碼 : cinfo.optimize_coding = TRUE;
- 編碼質量 : 調用 jpeg_set_quality 方法設置壓縮質量 ;
五、開始壓縮
1 . 開始壓縮 : 調用 jpeg_start_compress 方法 , 開始進行圖片壓縮工作 ;
2 . 函數原型 :
- j_compress_ptr cinfo 參數 : jpeg_compress_struct 結構指針 ;
- boolean write_all_tables 參數 : 設置 TRUE 參數, 表示將完整的圖片進行壓縮 ; 一般情況下都是設置 TRUE, 如果進行定制壓縮, 可以設置 FALSE ;
3 . 代碼示例 :
// 4. 開始壓縮 JPEG 格式圖片, 設置 TRUE 參數, 表示將完整的圖片進行壓縮// 一般情況下都是設置 TRUE, 如果進行定制壓縮, 可以設置 FALSEjpeg_start_compress(&cinfo, TRUE);六、循環寫入壓縮數據
1 . 寫入壓縮數據原理 : 使用函數庫的狀態變量, cinfo.next_scanline 作為循環控制變量 , 這樣就可以不同自己實現循環控制 , 為了保持代碼簡單, 每次傳遞一行圖像數據 ;
2 . 計算每行數據字節數 : 像素寬度乘以 333 , 333 表示每個像素點有 BGR 三個顏色值 , 每個顏色 111 字節 ;
int row_stride = imageWidth * 3;3 . 計算每次循環拷貝的行數據首地址 : uint8_t *pixels = data + cinfo.next_scanline * row_stride 是計算過程 ;
- data 是圖像的起始位置
- row_stride 是每一行的字節數
- cinfo.next_scanline 是當前的行數
- 計算出來的 pixels 指針, 指向要寫出行的首地址
4 . 循環控制變量自增 : jpeg_write_scanlines(&cinfo, row, 1) , 調用 jpeg_write_scanlines 方法后, cinfo.next_scanline 自動加 1 ;
5 . 代碼示例 :
// 每一個行的數據個數int row_stride = imageWidth * 3;// 指向圖像數據中的某一行數據JSAMPROW row[1];while (cinfo.next_scanline < cinfo.image_height) {/* 獲取一行圖像數據* data 是圖像的起始位置* row_stride 是每一行的字節數* cinfo.next_scanline 是當前的行數* 計算出來的 pixels 指針, 指向要寫出行的首地址*/uint8_t *pixels = data + cinfo.next_scanline * row_stride;row[0] = pixels;// 調用 jpeg_write_scanlines 方法后, cinfo.next_scanline 自動加 1jpeg_write_scanlines(&cinfo, row, 1);}七、完成圖片壓縮及收尾
1 . 完成圖片壓縮及收尾 :
-
調用 jpeg_finish_compress 結束圖片壓縮過程 ;
-
調用 fclose 關閉之前 fopen 打開的文件 ;
-
調用 jpeg_destroy_compress 方法銷毀之前使用的 JPEG 壓縮對象 ;
2 . 代碼示例 :
// 6. 完成圖片壓縮jpeg_finish_compress(&cinfo);// 7. 釋放相關資源fclose(outfile);jpeg_destroy_compress(&cinfo);八、libjpeg-turbo 圖片壓縮案例 ( 官方示例 )
在源碼 libjpeg-turbo-2.0.5/example.txt 文件中 , 有詳細的 JPEG 圖片壓縮流程 , 可以直接拷貝上述代碼進行使用 ;
點擊此處連接打開官方示例代碼
九、libjpeg-turbo 壓縮圖片代碼示例
/*** 壓縮 Jpeg 圖片** 完整的帶詳細注釋的代碼示例參考源碼 libjpeg-turbo-2.0.5/example.txt 示例文件* 里面有詳細的定義圖片壓縮的過程** @param data 要壓縮的圖片數據, 像素格式是 BGR* @param imageWidth 輸出的 JPEG 圖片寬度* @param imageHeight 輸出的 JPEG 圖片高度* @param compressQuality 輸出的 JPEG 圖片質量* @param filename 輸出文件路徑*/ void compressJpegFile(uint8_t *data, int imageWidth, int imageHeight,jint compressQuality, const char *filename) {// 1. 為 JPEG 圖片壓縮對象, 分配內存空間/* 該對象中存儲了 JPEG 壓縮參數, 還包含了指向工作空間的指針, JPEG 庫會在需要時分配該指針;* 該結構體可能會存在多個, 每個結構體對象都表示了一個壓縮或解壓縮的工作;* JPEG 對象 : jpeg_compress_struct 結構體和與其關聯的工作數據*/struct jpeg_compress_struct cinfo;/* 錯誤處理程序 : jpeg_error_mgr 結構體表示錯誤處理程序,* 將其單獨定義成一個結構體, 是因為應用經常需要提供一個專門的錯誤處理程序;* 處理處理機制 : 在這里我們采用最簡單的方法, 使用標準的錯誤處理程序,* 如果壓縮失敗, 在 stderr 上打印失敗信息, 并調用 exit() 退出程序 ;* 結構體聲明周期 : 該結構體的生命周期必須與 jpeg_compress_struct 結構體的生命周期保持一致,* 以免產生野指針問題 ;*/struct jpeg_error_mgr jerr;/* 為了防止 JPEG 壓縮對象初始化時出錯, 這里首先設置錯誤處理* 在內存不足時, 創建 jpeg_compress_struct 可能會失敗*/cinfo.err = jpeg_std_error(&jerr);// 初始化 JPEG 壓縮對象jpeg_create_compress(&cinfo);// 2. 打開文件, 準備向文件寫出二進制數據// w 代表寫出數據, b 代表二進制數據FILE *outfile;if ((outfile = fopen(filename, "wb")) == NULL) {fprintf(stderr, "can't open %s\n", filename);exit(1);}// 設置文件輸出jpeg_stdio_dest(&cinfo, outfile);// 3. 設置壓縮參數// 下面的四個參數是必須設置的參數// 設置圖片的寬度cinfo.image_width = imageWidth;// 設置圖片的高度cinfo.image_height = imageHeight;// 設置每個像素的顏色組件, BGR 3個cinfo.input_components = 3;// 輸入圖像數據的顏色空間cinfo.in_color_space = JCS_RGB;// 設置默認的壓縮參數, 該操作是函數庫的常規步驟// 設置該參數前需要設置 cinfo.in_color_space 輸入數據的顏色空間jpeg_set_defaults(&cinfo);// 打開哈夫曼編碼cinfo.optimize_coding = TRUE;// 設置非默認參數, 該方法設置質量jpeg_set_quality(&cinfo, compressQuality, 1);// 4. 開始壓縮 JPEG 格式圖片, 設置 TRUE 參數, 表示將完整的圖片進行壓縮// 一般情況下都是設置 TRUE, 如果進行定制壓縮, 可以設置 FALSEjpeg_start_compress(&cinfo, TRUE);// 5. 循環寫入數據/* 循環原理 : 使用函數庫的狀態變量, cinfo.next_scanline 作為循環控制變量* 這樣就可以不同自己實現循環控制* 為了保持簡單, 每次傳遞一行圖像數據*/// 每一個行的數據個數int row_stride = imageWidth * 3;// 指向圖像數據中的某一行數據JSAMPROW row[1];while (cinfo.next_scanline < cinfo.image_height) {/* 獲取一行圖像數據* data 是圖像的起始位置* row_stride 是每一行的字節數* cinfo.next_scanline 是當前的行數* 計算出來的 pixels 指針, 指向要寫出行的首地址*/uint8_t *pixels = data + cinfo.next_scanline * row_stride;row[0] = pixels;// 調用 jpeg_write_scanlines 方法后, cinfo.next_scanline 自動加 1jpeg_write_scanlines(&cinfo, row, 1);}// 6. 完成圖片壓縮jpeg_finish_compress(&cinfo);// 7. 釋放相關資源fclose(outfile);jpeg_destroy_compress(&cinfo); }
總結
以上是生活随笔為你收集整理的【Android 内存优化】Android 工程中使用 libjpeg-turbo 压缩图片 ( 初始化压缩对象 | 打开文件 | 设置压缩参数 | 写入压缩图像数据 | 完成压缩 | 释放资源 )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Android 内存优化】Androi
- 下一篇: 【Android 内存优化】Bitmap