You need to enable JavaScript to run this app.
veImageX

veImageX

复制全文
加载 SDK
Glide 接入 HEIF 解码库操作说明
复制全文
Glide 接入 HEIF 解码库操作说明

本文为您介绍 Glide 接入 veImageX 提供的 HEIF 解码库的接入流程,实现 HEIF 加载。

注意事项

  • 当前仅支持解码 HEIF 静图,HEIF 动图暂不支持。

  • 建议使用 4.0.0 及以上版本的 Glide。

  • 通过 Glide 使用解码库前,请确保已注册解码器插件

    说明

    解码器插件分为HeifByteBufferBitmapDecoderStreamHeifDecoder两种,分别可解码网络图和本地图。您在通过 Glide 使用 veImageX 解码库前,请确保已完成相应图片对应类型解码器的配置和注册操作。

前提条件

您已完成独立 HEIF 解码库的集成准备

添加 Maven 仓库

在 project 根目录下的build.gradle下配置服务,代码示例如下所示:

maven {
    url 'https://artifact.bytedance.com/repository/Volcengine/'
    // 最新版本号获取地址:https://artifact.bytedance.com/repository/Volcengine/
}

添加 SDK 依赖

说明

Glide 为开源图片库,建议您使用 4.0.0 及以上版本,具体版本号请见 Glide 版本历史

在 module 目录下的build.gradle文件中的dependencies中添加 SDK 依赖,代码示例如下所示。

dependencies {
    implementation("com.github.bumptech.glide:glide:xxx") // Glide 库(推荐 4.0.0 及以上版本)
    annotationProcessor("com.github.bumptech.glide:compiler:xxx") //Glide 库解码能力(推荐 4.0.0 及以上版本)
}

如果您使用 Kotlin 语言来编写 AppGlideModule,您需要使用 kapt 或者 ksp 插件实现编译处理,推荐您使用 ksp 插件。

  1. 在 gradle 项目文件中添加 ksp 插件

  2. dependencies中添加 SDK 依赖。

dependencies {
    implementation("com.github.bumptech.glide:glide:xxx") // Glide 库(推荐 4.0.0 及以上版本)
    ksp("com.github.bumptech.glide:ksp:xxx") // 使用 KSP 插件替代传统 Kotlin 注解处理器(推荐 4.0.0 及以上版本)
}
   

配置授权

在自定义的ApplicationonCreate方法中执行以下代码:

List<String> encodedAuthCode = new ArrayList<>();
encodedAuthCode.add("xxxxxxxxxxxxxxxxxxxxxxxxx");

InitConfig initConfig = new InitConfig(
        this,             // Application Context
        "000000",         // App id, 如实填写,您可在 独立 HEIF 解码库集成准备-获取 AppID 获取
        "sample",         // App Name,暂未用到,传入app名称
        "debug",          // channel,暂未用到,传入例如OPPO  
        "0.0.1",          // App的versionName, 如实填写
        "1",              // App的versionCode, 如实填写
        "48144589260",    // device id,暂未用到
        InitConfig.CHINA, // App 上线的区域,如实填写
        "M2ZmYzkzZjUtN2", // Token,如实填写,您可在 独立 HEIF 解码库集成准备-获取 Token 获取
        encodedAuthCode   // 授权码List<String>,如实填写,您可在 独立 HEIF 解码库集成准备-购买授权 获取
);
CloudControl.init(initConfig);

配置解码器插件

基于 HEIF 解码库配置 Glide 解码器插件,在 decode 方法中接入 veImageX HEIF 解码库,具体参考 Glide 官方文档中的自定义组件模块,代码示例如下所示。

说明

若 HEIF 图不包含 Alpha 通道,但仍选择使用Heif.toRgb565() 解码时,将可能导致最终图片解析异常。

配置网络图片解码插件

BufferDecoder 为网络图片解码插件,您可以使用该插件对公网可访问的 HEIF 图片进行解码。

class HeifByteBufferBitmapDecoder(bitmapPool: BitmapPool) : ResourceDecoder<ByteBuffer, Bitmap> {

    private val bitmapPool: BitmapPool

    init {
        this.bitmapPool = Preconditions.checkNotNull(bitmapPool)
    }

    override fun handles(source: ByteBuffer, options: Options): Boolean {
        val buffer = ByteBufferUtil.toBytes(source)
        return Heif.isHeif(buffer, buffer.size)
    }

    override fun decode(
        source: ByteBuffer,
        width: Int,
        height: Int,
        options: Options
    ): Resource<Bitmap>? {
        // 将 ByteBuffer 转为 buffer 数组
        val buffer = ByteBufferUtil.toBytes(source)
        var hasAlpha = true
        // 解析图片的基本信息
        val meta = Heif.parseSimpleMeta(buffer, buffer.size)
        meta ?: return null
        if (meta.size > 8) {
            // 是否有 alpha 通道 
            hasAlpha = meta[8] != 0
        }
        // 根据是否有 alpha 通道,选择解码成不同格式
        val heifData = if (hasAlpha) {
            // 包含,使用 heif toRgba 解码
            Heif.toRgba(buffer, buffer.size, false, 1, 0, 0, meta[1], meta[0])
        } else {
            // 不包含,使用 heif toRgb565 解码
            // 注意:如果有 alpha 通道的图片强行使用 toRgb565 方式解码,可能会造成图片解析异常。
            Heif.toRgb565(buffer, buffer.size, false, 1, 0, 0, meta[1], meta[0])
        }
        heifData ?: return null
        val config: Bitmap.Config = if (hasAlpha) {
            Bitmap.Config.ARGB_8888
        } else {
            Bitmap.Config.RGB_565
        }
        val bitmap = heifData.newBitmap(config)
       // 将 HEIF 图片解码为 Bitmap
        return BitmapResource.obtain(bitmap, bitmapPool)
    }
}

配置本地图片解码插件

StreamDecoder 为本地图片解码插件,您可以使用该插件对存储在手机本地的 HEIF 图片进行解码。

import android.graphics.Bitmap
import com.bumptech.glide.load.Options
import com.bumptech.glide.load.ResourceDecoder
import com.bumptech.glide.load.engine.Resource
import com.bumptech.glide.load.engine.bitmap_recycle.ArrayPool
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
import com.bytedance.fresco.nativeheif.Heif
import com.facebook.common.logging.FLog
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.io.InputStream

class StreamHeifDecoder(private val bitmapPool: BitmapPool, private val byteArrayPool: ArrayPool) :
    ResourceDecoder<InputStream, Bitmap> {

    private fun isHeif(source: InputStream, byteArrayPool: ArrayPool): Boolean {
        val arr = byteArrayPool.get(
            MAX_META,
            ByteArray::class.java
        )
        try {
            if (source.read(arr) == -1) {
                return false
            }
        } catch (e: IOException) {
            e.printStackTrace()
            return false
        }
        byteArrayPool.put(arr)

        if (arr == null) {
            return false
        }
        return Heif.isHeif(arr, arr.size)
    }

    override fun handles(source: InputStream, options: Options): Boolean {
        if (source.available() == 0) {
            return false
        }
        return isHeif(source, byteArrayPool)
    }

    override fun decode(
        source: InputStream,
        width: Int,
        height: Int,
        options: Options
    ): Resource<Bitmap>? {
        // 将 InputStream 转为 buffer 数组
        val buffer = inputStreamToBytes(source)
        var hasAlpha = true
        // 解析图片的基本信息
        val meta = Heif.parseSimpleMeta(buffer, buffer.size)
        FLog.d(TAG, "heifData meta = $meta")
        meta ?: return null
        if (meta.size > 8) {
            // 是否有 alpha 通道
            hasAlpha = meta[8] != 0
        }
        // 根据是否有 alpha 通道,选择解码成不同格式
        val heifData = if (hasAlpha) {
            // 包含,使用 heif toRgba 解码
            Heif.toRgba(buffer, buffer.size, false, 1, 0, 0, meta[1], meta[0])
        } else {
            // 不包含,使用 heif toRgb565 解码
            // 注意:如果有 alpha 通道的图片强行使用 toRgb565 方式解码,可能会造成图片解析异常。
            Heif.toRgb565(buffer, buffer.size, false, 1, 0, 0, meta[1], meta[0])
        }
        FLog.d(TAG, "heifData hasAlpha = $hasAlpha")
        heifData ?: return null
        val config: Bitmap.Config = if (hasAlpha) {
            Bitmap.Config.ARGB_8888
        } else {
            Bitmap.Config.RGB_565
        }
        val bitmap = heifData.newBitmap(config)
        return HeifBitmapResource(bitmapPool, bitmap)
    }

    private fun inputStreamToBytes(inputStream: InputStream): ByteArray {
        val buffer = ByteArrayOutputStream()
        val data = ByteArray(READ_SIZE)

        var nRead: Int
        while ((inputStream.read(data, 0, data.size).also { nRead = it }) != -1) {
            buffer.write(data, 0, nRead)
        }
        return buffer.toByteArray()
    }

    companion object {
        const val TAG: String = "StreamHeifDecoder"
        const val MAX_META = 5000
        private const val READ_SIZE = 10 * 1024
    }
}

StreamHeifDecoder 中的 HeifBitmapResource 类实现如下所示:

// # HeifBitmapResource

import android.graphics.Bitmap;

import androidx.annotation.NonNull;

import com.bumptech.glide.load.engine.Resource;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;


public class HeifBitmapResource implements Resource<Bitmap> {
    private final BitmapPool bitmapPool;
    private final Bitmap mBitmap;

    public HeifBitmapResource(BitmapPool bitmapPool, Bitmap mBitmap) {
        this.bitmapPool = bitmapPool;
        this.mBitmap = mBitmap;
    }

    @NonNull
    @Override
    public Class<Bitmap> getResourceClass() {
        return Bitmap.class;
    }

    @NonNull
    @Override
    public Bitmap get() {
        return mBitmap;
    }


    @Override
    public int getSize() {
        return mBitmap.getByteCount();
    }

    @Override
    public void recycle() {
        bitmapPool.put(mBitmap);
    }
}

注册解码器插件

使用 prepend 方法注册 HeifByteBufferBitmapDecoder 解码器和 StreamHeifDecoder 解码器,并将其指定为解码 HEIF 图像时的最高优先级解码器。以便在使用 Glide 解码 HEIF 图像时,默认会优先使用该自定义解码器。

说明

对于加载某些在线 HEIF 图像出现的异常,建议参考使用 Glide 自定义解码器插件时处理 HEIF 图时加载异常的排查建议处理。

@GlideModule
open class HeifGlideModule : AppGlideModule() {
    override fun registerComponents(
        context: Context, glide: Glide, registry: Registry
    ) {
        registerStaticImageDecoder(glide, registry)
    }

    /**
     * 注册静态图片解码器
     */
    private fun registerStaticImageDecoder(glide: Glide, registry: Registry) {
        val byteBufferBitmapDecoder = HeifByteBufferBitmapDecoder(glide.bitmapPool)
        // 优先使用 prepend 注册的解码器
        // 静态图片解码器:buffer decoder,处理网络图片加载
        registry.prepend(
            Registry.BUCKET_BITMAP,
            ByteBuffer::class.java,
            Bitmap::class.java,
            byteBufferBitmapDecoder
        )

        // 静态图片解码器:stream decoder,处理本地图片加载
        registry.prepend(
            Registry.BUCKET_BITMAP, InputStream::class.java, Bitmap::class.java,
            StreamHeifDecoder(glide.bitmapPool, glide.arrayPool)
        )
    }

如果您是在单独的模块中封装 Glide 框架,而不是在主应用程序模块中使用 Glide,请您将 AppGlideModule 更改为 LibraryGlideModule

使用

参考 Glide 官方文档加载 HEIF 图片,具体如下所示。

Glide.with(context)
    .load("http://test.com/demo.heic")// 网络 heic 图片的加载地址,加载前确保已配置和注册 BufferDecoder 解码器
  //.load("file:///storage/emulated/0/demo.heic") // 本地 heic 图的加载地址,加载前确保已配置和注册 StreamDecoder 解码器
    .into(imageView)

常见问题

使用 Glide 自定义解码器插件时处理 HEIF 图时加载异常的排查建议。

  1. 检查依赖配置:确保HeifGlideModule类在编译构建后成功生成有效的注册代码。如果HeifGlideModule没有被其他类引用到,通常是因为 Gradle 文件配置的问题,请检查是否正确配置了 Glide 依赖,确保配置正确并成功生成目标文件。

  2. 检查注册方法:确保使用registry.prepend()接口进行解码器注册。如果您使用的是 append 方法注册,那么当 Glide 自带的解码器能够处理 HEIF 图时,将会优先使用自带解码器拦截,最终导致自定义的解码器无法得到执行。

  3. 检查缓存策略:在 Glide 中,当您需要使用自定义解码器插件处理 HEIF 时,如果设置了跳过磁盘缓存策略,Glide无法优先使用自定义解码器对图片进行解码,而会默认使用自带的解码器,这可能导致 HEIF 图像解码异常。此时,建议您采取以下操作进行排查和处理:

    1. 检查缓存策略:请确保没有设置跳过磁盘缓存的相关策略,如.diskCacheStrategy(DiskCacheStrategy.RESOURCE).diskCacheStrategy(DiskCacheStrategy.NONE)。这样 Glide 才能将图像保存到磁盘缓存,并使用自定义解码器进行解码。

    2. 清理缓存:如果之前已经设置了跳过磁盘缓存策略,或者存在旧的缓存数据,建议清理缓存。通过清理缓存,可以删除旧的缓存文件,以防止其对解码效果产生影响。您可以使用 Glide 提供的缓存清理方法或手动删除相关缓存文件。

最近更新时间:2024.10.17 11:43:27
这个页面对您有帮助吗?
有用
有用
无用
无用