1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > 【Android 内存优化】Android 原生 API 图片压缩原理 ( Bitmap_compress 方法

【Android 内存优化】Android 原生 API 图片压缩原理 ( Bitmap_compress 方法

时间:2020-11-27 22:16:22

相关推荐

【Android 内存优化】Android 原生 API 图片压缩原理 ( Bitmap_compress 方法

文章目录

一、 图片质量压缩方法二、 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 函数库 )

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。