【HDR学习】图片解码(三)

  • 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

相关链接:

Android图片之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;
  }

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值