【Android 内存优化】Android 原生 API 图片压缩原理 ( Bitmap_compress 方法解析 | Skia 二维图形库 | libjpeg 函数库 | libpng 函数库 )
文章目錄
- 一、 圖片質量壓縮方法
- 二、 Skia 二維圖形庫
- 三、 libjpeg、libpng 函數庫引入
在博客 【Android 內存優化】圖片文件壓縮 ( Android 原生 API 提供的圖片壓縮功能能 | 圖片質量壓縮 | 圖片尺寸壓縮 ) 簡要介紹了 圖片文件壓縮格式 , 以及 Android 提供的圖片質量 , 尺寸壓縮原生 API ;
在博客 【Android 內存優化】Android 原生 API 圖片壓縮代碼示例 ( PNG 格式壓縮 | JPEG 格式壓縮 | WEBP 格式壓縮 | 動態權限申請 | Android10 存儲策略 ) 主要使用了上述 Android 原生 API 壓縮圖片功能進行圖片壓縮 ;
在博客 【Android 內存優化】Android 原生 API 圖片壓縮原理 ( 圖片質量壓縮方法 | 查找 Java 源碼中的 native 方法對應的 C++ 源碼 ) 中主要查找 Bitmap.java 對應的 Native 層的 C++ 類 Bitmap.cpp 源碼文件 , 并分析了其動態注冊 Native 方法的過程 ;
本博客中將分析 Bitmap.cpp 中的源碼 ;
一、 圖片質量壓縮方法
Java 對應方法 :
參數分析 :
- long nativeBitmap 參數 : Native 層的 Bitmap 指針 ;
- int format 參數 : 壓縮格式格式 ;
- int quality 參數 : 壓縮質量 ;
- OutputStream stream 參數 : 輸出流 ;
- byte[] tempStorage 參數 : 暫時的存儲區 ;
C++ 對應方法 :
參數分析 :
- jlong bitmapHandle 參數 : Native 層的 Bitmap 指針 ;
- jint format 參數 : 壓縮格式格式 ;
- jint quality 參數 : 壓縮質量 ;
- jobject jstream 參數 : 輸出流 ;
- jbyteArray jstorage 參數 : 暫時的存儲區 ;
源碼位置 \frameworks\base\core\jni\android\graphics\Bitmap.cpp
上述 Bitmap.cpp 中的 Bitmap_compress 方法中 , 最終調用的 SkImageEncoder 的 encodeStream 方法 ;
SkImageEncoder 不是最終調用的類 , 而是根據不同的圖片壓縮格式 , 調用對應的類 , 如果最終壓縮格式是 JPEG 格式 , 那么就會調用 SkJPEGImageEncoder 方法 ,
在下面的 SkImageEncoder.h 中聲明了 SkImageEncoder 類 , 特別注意下面定義的 virtual bool onEncode 方法 , 是虛函數 , 需要在子類中實現該函數 ;
#ifndef SkImageEncoder_DEFINED #define SkImageEncoder_DEFINED#include "SkTypes.h" #include "SkTRegistry.h"class SkBitmap; class SkData; class SkWStream;class SkImageEncoder { public:enum Type {kUnknown_Type,kBMP_Type,kGIF_Type,kICO_Type,kJPEG_Type,kPNG_Type,kWBMP_Type,kWEBP_Type,kKTX_Type,};static SkImageEncoder* Create(Type);virtual ~SkImageEncoder();/* Quality ranges from 0..100 */enum {kDefaultQuality = 80};SkData* encodeData(const SkBitmap&, int quality);bool encodeFile(const char file[], const SkBitmap& bm, int quality);bool encodeStream(SkWStream* stream, const SkBitmap& bm, int quality);static SkData* EncodeData(const SkBitmap&, Type, int quality);static bool EncodeFile(const char file[], const SkBitmap&, Type,int quality);static bool EncodeStream(SkWStream*, const SkBitmap&, Type,int quality);protected:// 特別注意 : 該函數是個虛函數 , 需要在子類實現中實現該方法virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) = 0; };源碼位置 \external\skia\include\core\SkImageEncoder.h
在 SkImageEncoder.cpp 中實現了上述方法 , 其中壓縮文件的方法 SkImageEncoder::encodeStream , 在該方法中調用了 onEncode 方法 , 該函數是虛函數 , 需要在子類沖實現 ;
#include "SkImageEncoder.h" #include "SkBitmap.h" #include "SkStream.h" #include "SkTemplates.h"SkImageEncoder::~SkImageEncoder() {}// 在該方法中調用了 onEncode 虛函數方法 , 該方法需要在子類中實現 bool SkImageEncoder::encodeStream(SkWStream* stream, const SkBitmap& bm,int quality) {quality = SkMin32(100, SkMax32(0, quality));return this->onEncode(stream, bm, quality); }// ... 省略部分代碼源碼位置 \external\skia\src\images\SkImageEncoder.cpp
下面的 SkJPEGImageEncoder 類是 SkImageEncoder 子類 , 該類主要處理 JPEG 格式編碼操作 ; 在重寫的 onEncode 方法中 , 主要使用 libjpeg 函數庫實現 JPEG 圖像編碼 ;
// SkJPEGImageEncoder 是 SkImageEncoder 子類 , 共有繼承 class SkJPEGImageEncoder : public SkImageEncoder { protected:// 該方法中調用了大量 libjpeg 庫的函數virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) { #ifdef TIME_ENCODESkAutoTime atm("JPEG Encode"); #endifSkAutoLockPixels alp(bm);if (NULL == bm.getPixels()) {return false;}jpeg_compress_struct cinfo;skjpeg_error_mgr sk_err;skjpeg_destination_mgr sk_wstream(stream);// allocate these before set call setjmpSkAutoMalloc oneRow;SkAutoLockColors ctLocker;cinfo.err = jpeg_std_error(&sk_err);sk_err.error_exit = skjpeg_error_exit;if (setjmp(sk_err.fJmpBuf)) {return false;}// Keep after setjmp or mark volatile.const WriteScanline writer = ChooseWriter(bm);if (NULL == writer) {return false;}jpeg_create_compress(&cinfo);cinfo.dest = &sk_wstream;cinfo.image_width = bm.width();cinfo.image_height = bm.height();cinfo.input_components = 3; #ifdef WE_CONVERT_TO_YUVcinfo.in_color_space = JCS_YCbCr; #elsecinfo.in_color_space = JCS_RGB; #endifcinfo.input_gamma = 1;jpeg_set_defaults(&cinfo);jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */); #ifdef DCT_IFAST_SUPPORTEDcinfo.dct_method = JDCT_IFAST; #endifjpeg_start_compress(&cinfo, TRUE);const int width = bm.width();uint8_t* oneRowP = (uint8_t*)oneRow.reset(width * 3);const SkPMColor* colors = ctLocker.lockColors(bm);const void* srcRow = bm.getPixels();while (cinfo.next_scanline < cinfo.image_height) {JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */writer(oneRowP, srcRow, width, colors);row_pointer[0] = oneRowP;(void) jpeg_write_scanlines(&cinfo, row_pointer, 1);srcRow = (const void*)((const char*)srcRow + bm.rowBytes());}jpeg_finish_compress(&cinfo);jpeg_destroy_compress(&cinfo);return true;} };源碼位置 \external\skia\src\images\SkImageDecoder_libjpeg.cpp
二、 Skia 二維圖形庫
Skia 是 C++ 開源二維圖形庫 , 用于操作二維圖形 , 提供一系列 2D 圖形處理 API , 在 Chrom 瀏覽器 , 安卓手機 , 狐火瀏覽器中使用該圖形庫作為二維圖形引擎 ;
Skia 相關網址 :
- 官方網站 , 國內無法訪問 ;
- 源碼地址 , 國內無法訪問 ;
- GitHub 源碼鏡像
三、 libjpeg、libpng 函數庫引入
libjpeg、libpng 函數庫引入 : Android 中的 Bitmap 就使用到了 Skia 引擎 , Android 中的 Skia 功能不全 , 經過刪減了 ;
-
處理 JPEG 格式圖像基于 libjpeg 函數庫 ;
-
處理 PNG 格式圖形基于 libpng 函數庫 ;
總結
以上是生活随笔為你收集整理的【Android 内存优化】Android 原生 API 图片压缩原理 ( Bitmap_compress 方法解析 | Skia 二维图形库 | libjpeg 函数库 | libpng 函数库 )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Android 内存优化】Androi
- 下一篇: 【Android 内存优化】Androi