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解码步骤如下:

  1. build.gradle的repositories节点中添加阿里云Maven仓库配置。
    maven { url 'https://maven.aliyun.com/repository/public' }
  2. 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官方文档

    1. 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'
    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);
          }
      }
    3. 注册解码器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);
          }
      }
    4. 加载显示HEIF图片。
      private void loadHeif(Context context, ImageView imageView, File file) {
          Glide.with(context).asBitmap().load(file).into(imageView)
      }
  • 场景三:使用Fresco集成解码库解码

    Fresco是Facebook开源的一个知名Android图片缓存库。更多信息,请参见Fresco官方文档

    1. build.gradle中接入Fresco。
      implementation 'com.aliyun:libheif:0.0.2'
      implementation 'com.facebook.fresco:fresco:2.6.0'
    2. 创建自定义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());
          }
      }
    3. 加载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);
      }

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解码。

说明 请确保版本号为4.13.0及以上版本。
implementation "com.github.bumptech.glide:avif-integration:4.13.2"        

Fresco集成解码库

关于Fresco如何集成解码库,请参见自定义解码器相关文档