日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

【Android 内存优化】Android 原生 API 图片压缩原理 ( Bitmap_compress 方法解析 | Skia 二维图形库 | libjpeg 函数库 | libpng 函数库 )

發布時間:2025/6/17 Android 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【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 參數 : 暫時的存儲區 ;
private static native boolean nativeCompress(long nativeBitmap, int format,int quality, OutputStream stream,byte[] tempStorage);

C++ 對應方法 :

參數分析 :

  • jlong bitmapHandle 參數 : Native 層的 Bitmap 指針 ;
  • jint format 參數 : 壓縮格式格式 ;
  • jint quality 參數 : 壓縮質量 ;
  • jobject jstream 參數 : 輸出流 ;
  • jbyteArray jstorage 參數 : 暫時的存儲區 ;
static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle,jint format, jint quality,jobject jstream, jbyteArray jstorage) {// 獲取 Bitmap 指針SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);SkImageEncoder::Type fm;// 判斷圖片壓縮格式 , 給 SkImageEncoder::Type fm 局部變量賦值switch (format) {// JPEG 格式case kJPEG_JavaEncodeFormat:fm = SkImageEncoder::kJPEG_Type;break;// PNG 格式case kPNG_JavaEncodeFormat:fm = SkImageEncoder::kPNG_Type;break;// WEBP 格式case kWEBP_JavaEncodeFormat:fm = SkImageEncoder::kWEBP_Type;break;// 如果傳入未知格式 , 直接返回 錯誤信息 default:return JNI_FALSE;}bool success = false;if (NULL != bitmap) {SkAutoLockPixels alp(*bitmap);if (NULL == bitmap->getPixels()) {return JNI_FALSE;}SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);if (NULL == strm) {return JNI_FALSE;}// 創建圖片編碼器 , 需要根據傳入的 fm 編碼類型創建SkImageEncoder* encoder = SkImageEncoder::Create(fm);if (NULL != encoder) {// ★ 這是圖片壓縮的核心方法success = encoder->encodeStream(strm, *bitmap, quality);delete encoder;}delete strm;}return success ? JNI_TRUE : JNI_FALSE; }

源碼位置 \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 函数库 )的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。