Android客户端默认不支持解码HEIC和AVIF格式。如果您需要解码高级图片处理后的HEIC和AVIF格式,您可以配置Android客户端的相关依赖,以便支持AVIF和HEIC图片解码。
Android接入HEIC解码说明
高效率图像格式HEIF(High Efficiency Image Format )是一种压缩图片格式,通过使用更现代的压缩算法,在相同质量的前提下,HEIF文件大小是JPEG文件的40%左右。
阿里云高性能HEIC解码库在开源的libheif和libde265基础上进行二次开发,针对ARM平台做了大量优化,参考了其它同类开源库优点,在解码链路上的各个环节进行优化,显著地提升了运行速度。相比原始的开源版本,解码的效率有数倍的提升。阿里云高性能HEIC解码库代码链接,请参见Github。
Android客户端接入
通过Android客户端接入HEIC解码步骤如下:
- 在build.gradle的repositories节点中添加阿里云Maven仓库配置。
maven { url 'https://maven.aliyun.com/repository/public' }
- 在build.gradle的dependencies节点添加依赖。
implementation 'com.aliyun:libheif:0.0.2'
解码HEIC图片
- 场景一:直接使用API接口解码
/** * get info from heic picture * @param info heif info size & frame num * @param length the length of effective file memory * @param filebuf file pointer in memory * @return bool, if true, get info success */ fun getInfo(info: HeifInfo, length: Long, filebuf: ByteArray?): Boolean /** * @param length the length of byteArray * @param fileBuf file's byte array * @param bitmap the decode bitmap * @return decode success or not **/ fun toRgba(length: Long, fileBuf: ByteArray, bitmap: Bitmap): Boolean
以下以将asset目录下的test.heic解码成Bitmap为例为您进行介绍。
val image = findViewById<ImageView>(R.id.image); val inputStream = assets.open("test.heic") val buffer = ByteArray(8192) var bytesRead: Int val output = ByteArrayOutputStream() while (inputStream.read(buffer).also { bytesRead = it } != -1) { output.write(buffer, 0, bytesRead) } val heifInfo = HeifInfo() val fileBuffer: ByteArray = output.toByteArray() HeifNative.getInfo(heifInfo, fileBuffer.size.toLong(), fileBuffer) // 单帧图片, 获取frameList的第一个元素即图片高宽。 val heifSize = heifInfo.frameList.first() // 根据高宽创建好图片。 val bitmap = Bitmap.createBitmap( heifSize.width, heifSize.height, Bitmap.Config.ARGB_8888) // 解码。 val heifBuffer = HeifNative.toRgba(fileBuffer.size.toLong(), fileBuffer, bitmap) image.setImageBitmap(bitmap)
- 场景二:使用Glide集成解码库解码
Glide是一个知名开源的图片缓存库。更多信息,请参见Glide官方文档。
- build.gradle中接入Glide。
implementation 'com.aliyun:libheif:0.0.2' implementation 'com.github.bumptech.glide:glide:4.13.2' implementation 'com.github.bumptech.glide:compiler:4.13.2'
- 自定义HEIF解码器,在decode方法中接入阿里云高性能HEIC解码库,具体可以参考Glide官方文档中的自定义组件模块。
public final class HeifByteBufferBitmapDecoder implements ResourceDecoder<ByteBuffer, Bitmap> { private final BitmapPool bitmapPool; public HeifByteBufferBitmapDecoder(BitmapPool bitmapPool) { this.bitmapPool = Preconditions.checkNotNull(bitmapPool); } @Override public Resource<Bitmap> decode(ByteBuffer byteBuffer, int width, int height, Options options) { // HEIF图片解码 byte[] buffer = ByteBufferUtil.toBytes(byteBuffer); HeifInfo heifInfo = new HeifInfo(); HeifNative.getInfo(heifInfo, buffer.length, buffer); HeifSize heifSize = heifInfo.getFrameList().get(0); Bitmap bitmap = Bitmap.createBitmap(heifSize.getWidth(), heifSize.getHeight(), Bitmap.Config.ARGB_8888); HeifNative.toRgba(buffer.length, buffer, bitmap); Log.e(TAG,"heif decoderTime:" + (endTime-startTime)/1000); return BitmapResource.obtain(bitmap, bitmapPool); } @Override public boolean handles(ByteBuffer byteBuffer, Options options) { // 判断是否Heif文件 byte[] buffer = ByteBufferUtil.toBytes(byteBuffer); return HeifNative.isHeic(buffer.length, buffer); } }
- 注册解码器Module。
@GlideModule public class HeifGlideModule extends LibraryGlideModule { @Override public void registerComponents( @NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) { HeifByteBufferBitmapDecoder byteBufferBitmapDecoder = new HeifByteBufferBitmapDecoder(glide.getBitmapPool()); registry.append(ByteBuffer.class, Bitmap.class, byteBufferBitmapDecoder); } }
- 加载显示HEIF图片。
private void loadHeif(Context context, ImageView imageView, File file) { Glide.with(context).asBitmap().load(file).into(imageView) }
- build.gradle中接入Glide。
- 场景三:使用Fresco集成解码库解码
Fresco是Facebook开源的一个知名Android图片缓存库。更多信息,请参见Fresco官方文档。
- build.gradle中接入Fresco。
implementation 'com.aliyun:libheif:0.0.2' implementation 'com.facebook.fresco:fresco:2.6.0'
- 创建自定义HEIF解码器,在decode方法中接入阿里云高性能HEIC解码库。
public class FrescoHeifDecoder implements ImageDecoder { private File mFile; private Uri mUri; public FrescoHeifDecoder(File file) { this.mFile = file; } public FrescoHeifDecoder(Uri uri) { this.mUri = uri; } @Override public CloseableImage decode( EncodedImage encodedImage, int length, QualityInfo qualityInfo, ImageDecodeOptions options) { ImageRequest imageRequest = null; if (mFile != null) { imageRequest = ImageRequest.fromFile(mFile); } if (mUri != null) { imageRequest = ImageRequest.fromUri(mUri); } try { // 获取缓存图片 CacheKey cacheKey = DefaultCacheKeyFactory.getInstance() .getEncodedCacheKey(imageRequest, null); FileCache fileCache = ImagePipelineFactory.getInstance() .getMainFileCache(); BinaryResource resource = fileCache.getResource(cacheKey); File file; if (resource == null) { file = mFile; } else { file = ((FileBinaryResource) resource).getFile(); } // 解码HEIF图片 ByteBuffer bufferFromFile = ByteBufferUtil.fromFile(file); HeifInfo heifInfo = new HeifInfo(); HeifNative.getInfo(heifInfo, file.length(), bytes); HeifSize heifSize = heifInfo.getFrameList().get(0); Bitmap bitmap = Bitmap.createBitmap(heifSize.getWidth(), heifSize.getHeight(), Bitmap.Config.ARGB_8888); HeifNative.toRgba(file.length(), bytes, bitmap); return new CloseableStaticBitmap( pinBitmap(bitmap), qualityInfo, encodedImage.getRotationAngle(), encodedImage.getExifOrientation()); } catch (Exception e) { } return null; } public CloseableReference pinBitmap(Bitmap bitmap) { BitmapCounter mUnpooledBitmapsCounter = BitmapCounterProvider.get(); Preconditions.checkNotNull(bitmap); return CloseableReference.of(bitmap, mUnpooledBitmapsCounter.getReleaser()); } }
- 加载HEIF图片。
private void loadHeif(SimpleDraweeView simpleDraweeView, File file) { ImageRequest request = ImageRequestBuilder.newBuilderWithSource(Uri.fromFile(file)) .setImageDecodeOptions(ImageDecodeOptions.newBuilder() .setCustomImageDecoder(new FrescoHeifDecoder(file)).build()) .build(); AbstractDraweeController controller = Fresco.newDraweeControllerBuilder().setImageRequest(request).build(); simpleDraweeView.setController(controller); }
- build.gradle中接入Fresco。
Android接入AVIF解码说明
AVIF是一种基于AV1视频编码的新图像格式,相对于JPEG,Wep等图片格式压缩率更高,并且画面细节更好。AVIF通过使用更现代的压缩算法,在相同质量的前提下,AVIF文件大小是JPEG文件的35%左右。
AVIF的解码可以用采用官方解码库libavif。libavif已经封装好了Android的相关接口,相关代码在android_jni目录下。关于libavif代码的更多信息,请参见Github。
直接解码
直接调用解码方法即可将ByteBuffer进行解码。
/**
* Decodes the AVIF image into the bitmap.
*
* @param encoded The encoded AVIF image. encoded.position() must be 0.
* @param length Length of the encoded buffer.
* @param bitmap The decoded pixels will be copied into the bitmap.
* @return true on success and false on failure. A few possible reasons for failure are: 1) Input
* was not valid AVIF. 2) Bitmap was not large enough to store the decoded image.
*/
public static native boolean decode(ByteBuffer encoded, int length, Bitmap bitmap);
Glide集成解码库
Glide自定义Module已经支持AVIF,源码参考其代码仓库Github。
从4.13.0版本开始支持,只需要添加如下依赖即可使Glide支持AVIF解码。
implementation "com.github.bumptech.glide:avif-integration:4.13.2"
Fresco集成解码库
关于Fresco如何集成解码库,请参见自定义解码器相关文档。