-
BitmapFactory
在Android P之前,我们需要使用Drawable或BitmapFactory类中的静态方法,来分别加载Drawable、Bitmap。
// 从文件中加载
Drawable.createFromPath(pathName)
// 从asset中加载
Drawable.createFromStream(context.assets.open(assetFileName), "")
// 从文件中加载
BitmapFactory.decodeFile(pathName)
// 从asset中加载
BitmapFactory.decodeStream(context.assets.open(assetFileName))
// 从 byte array中加载
BitmapFactory.decodeByteArray(data, offset, length, opts)
//显示Drawable
imageView.setImageDrawable(drawable)
// 显示 bitmap
imageView.setImageBitmap(bitmap)
Bitmap和Drawable的关系、区别:
Bitmap
- 称作位图,一般位图的文件格式后缀为bmp
Drawable
- 作为Android通用的图形对象,它可以装载常用格式的图像
比如GIF、PNG、JPG,当然也支持BMP,还提供一些高级的可视化对象,比如渐变、图形等。
Bitmap是Drawable . Drawable不一定是Bitmap
Drawable在内存占用和绘制速度这两个非常关键的点上胜过Bitmap
相关链接:
-
ImageDecoder
早期的Android只支持三种图像格式,分别是JPEG、PNG和GIF。后安卓阵营的谷歌推出了WebP格式,而iOS阵营的苹果推出了HEIF格式。尽管WebP与HEIF出自不同的厂商,但它俩都具备了下列的优异特性:
1、支持透明背景;(JPEG不支持透明背景)
2、支持动画效果;(JPEG和PNG不支持动画效果)
3、支持有损压缩;(PNG和GIF不支持有损压缩,因此它们的图片体积较大)
ImageDecoder正是Android9推出的新型图像解码器,它不但兼容常规的JPEG和PNG图片,还适配GIF、WebP、HEIF的动图效果,可谓新老图片类型一网打尽。利用图像解码器加载并显示图片的步骤分为以下三步:
1、调用ImageDecoder的createSource方法,从指定地方获得数据源;
//创建文件夹下的某个片源
var source = ImageDecoder.createSource(resources, R.drawable.ic_launcher_background)
//创建assets下的源
var source = ImageDecoder.createSource(assets, "")
2、调用ImageDecoder的decodeDrawable方法,从数据源解码得到Drawable类型的图形信息;
var decodeDrawable = ImageDecoder.decodeDrawable(source)
var decodeBitmap = ImageDecoder.decodeBitmap(source)
3、调用图像视图的setImageDrawable,设置图像视图的图形对象;
imageview.setImageBitmap(decodeBitmap);
imageview.setImageDrawable(decodeDrawable);
-
三方库实现
主要介绍针对带有HDR效果的的AVIF&HEIC图片解码
-
libheif库
【概述】
libheif是一个比较常见的 C++ 解析 heic 格式图片的开源库,可实现对HEIF和AVIF(AV1图像文件格式)文件格式解码和编码。
HEIF和AVIF是新的图像文件格式,分别采用HEVC (h.265)或AV1图像编码,以获得目前可能的最佳压缩比。
libheif开源库使用libde265进行HEIF图像解码,使用x265进行编码。除此之外,支持插件安装,将AVIF,libaum、dav1d、svt-av1或rav1e用作编解码器。
【解码器支持】
- 平铺图像
- alpha通道
- 缩略图
- 读取EXIF和XMP元数据
- 读取深度通道
- 文件中的多个图像
- 图像变换(裁剪、镜像、旋转)
- 叠加图像
- 插件功能,可在添加其他格式(AVC、JPEG)文件的编解码器。如下所示。
- 读取颜色配置文件
- 十六进制图像(10和12位,色度4:2:2)
// libheif库中插件管理:\libheif\plugins\CMakeLists.txt set(X265_sources heif_encoder_x265.h heif_encoder_x265.cc) set(X265_extra_plugin_sources) plugin_compilation(x265 X265 X265 X265) set(LIBDE265_sources heif_decoder_libde265.cc heif_decoder_libde265.h) set(LIBDE265_extra_plugin_sources ../error.cc) plugin_compilation(libde265 LIBDE265 LIBDE265 LIBDE265) set(DAV1D_sources heif_decoder_dav1d.cc heif_decoder_dav1d.h) set(DAV1D_extra_plugin_sources ../common_utils.cc ../common_utils.h) plugin_compilation(dav1d DAV1D DAV1D DAV1D) set(AOM_DECODER_sources heif_decoder_aom.cc heif_decoder_aom.h) set(AOM_DECODER_extra_plugin_sources) plugin_compilation(aomdec AOM AOM_DECODER AOM_DECODER) set(AOM_ENCODER_sources heif_encoder_aom.cc heif_encoder_aom.h) set(AOM_ENCODER_extra_plugin_sources ../error.cc ../common_utils.cc ../common_utils.h) plugin_compilation(aomenc AOM AOM_ENCODER AOM_ENCODER) set(SvtEnc_sources heif_encoder_svt.cc heif_encoder_svt.h) set(SvtEnc_extra_plugin_sources) plugin_compilation(svtenc SvtEnc SvtEnc SvtEnc) set(RAV1E_sources heif_encoder_rav1e.cc heif_encoder_rav1e.h) set(RAV1E_extra_plugin_sources ../box.cc ../error.cc) plugin_compilation(rav1e RAV1E RAV1E RAV1E)
【编码器支持】
- 质量可调的有损压缩
- 无损压缩
- alpha通道
- 缩略图
- 将多个图像保存到文件
- 保存EXIF和XMP元数据
- 手写颜色配置文件
- 10位和12位图像
- 单色图像
-
API
heif.h头文件已经列出有哪些开放的API,以下对几个常用的API使用场景做出示例。
【图片解码】
解码器通过同一API自动支持HEIF和AVIF。不需要对现有代码进行更改即可支持AVIF。
heif_context* ctx = heif_context_alloc();
heif_context_read_from_file(ctx, input_filename, nullptr);
// get a handle to the primary image
heif_image_handle* handle;
heif_context_get_primary_image_handle(ctx, &handle);
// decode the image and convert colorspace to RGB, saved as 24bit interleaved
heif_image* img;
heif_decode_image(handle, &img, heif_colorspace_RGB, heif_chroma_interleaved_RGB, nullptr);
int stride;
const uint8_t* data = heif_image_get_plane_readonly(img, heif_channel_interleaved, &stride);
【图片改写】
heif_context* ctx = heif_context_alloc();
// get the default encoder
heif_encoder* encoder;
heif_context_get_encoder_for_format(ctx, heif_compression_HEVC, &encoder);
// set the encoder parameters
heif_encoder_set_lossy_quality(encoder, 50);
// encode the image
heif_image* image; // code to fill in the image omitted in this example
heif_context_encode_image(ctx, image, encoder, nullptr, nullptr);
heif_encoder_release(encoder);
heif_context_write_to_file(context, "output.heic");
【获取EXIF data】
heif_item_id exif_id;
int n = heif_image_handle_get_list_of_metadata_block_IDs(image_handle, "Exif", &exif_id, 1);
if (n==1) {
size_t exifSize = heif_image_handle_get_metadata_size(image_handle, exif_id);
uint8_t* exifData = malloc(exifSize);
struct heif_error error = heif_image_handle_get_metadata(image_handle, exif_id, exifData);
}
-
编译&使用
Clion工具打开压缩包中的工程,编译即可。开发过程可以参考
libheif/examples/heif_convert.cc
来完成一个简单的对 HEIC 格式图片的解析、解码的过程。
#include <libheif/heif.h>
bool decodeheif(string filename) {
// 类似于 ffmpeg AVFormatContext,heif 也需要一个上下文环境
heif_context* heif_ctx_ = heif_context_alloc();
if (!heif_ctx_) return false;
// 读取文件
heif_error error = heif_context_read_from_file(heif_ctx_, filename.c_str(), nullptr);
if (error.code != heif_error_Ok) return false;
// 获取图片句柄(handle)
heif_image_handle *heif_handle_ = nullptr;
error = heif_context_get_primary_image_handle(heif_ctx_, &heif_handle_);
if (error.code != heif_error_Ok) return false;
// 获取图片宽高,注意,由于之前我们是获取的是主图片的句柄,因此这里获取的是当前句柄下图片的宽高
width_ = heif_image_handle_get_width(heif_handle_);
height_ = heif_image_handle_get_height(heif_handle_);
// 解码,注意,这里需要再申请一个 heif_image 的指针变量,并将其地址传入该函数,即作为传出参数使用。
// 此外,这里的 heif_colorspace_RGB 也可以用 heif_colorspace_YCbCr,
// 同样地,后面的 heif_chroma_interleaved_RGBA 也可以用 heif_chroma_420 代替,
// 来获取 YUV 420 的图像数据,可能解码效率上会快很多
heif_image* heif_img_ = nullptr;
error = heif_decode_image(heif_handle_, &heif_img_, heif_colorspace_RGB, heif_chroma_interleaved_RGBA, nullptr);
if (error.code != heif_error_Ok) return false;
int stride;
// 解码完成之后,还需要调用下面的方法来获取解码数据的只读地址。拿到地址后如何使用,则看自己的了。
const uint8_t *data = heif_image_get_plane_readonly(heif_img_, heif_channel_interleaved, &stride);
if (data == nullptr) return false;
}
对 libheif 相关环境变量内存的释放也很简单:
if (heif_ctx_) {
heif_context_free(heif_ctx_);
heif_ctx_ = nullptr;
}
if (heif_handle_) {
heif_image_handle_release(heif_handle_);
heif_handle_ = nullptr;
}
if (heif_img_) {
heif_image_release(heif_img_);
heif_img_ = nullptr;
}