使用Image\_NativeModule完成动图解码
===========================

使用Image\_NativeModule解码GIF、WebP等动图资源，获取动图帧数、帧延迟时间，并根据业务需要选择全量解码或逐帧解码方式获取PixelMap。

动图解码主要包含以下流程：

1.  创建ImageSource实例。
2.  调用接口获取图片帧数，判断图片是否为动图。
3.  获取每帧的延迟时间。
4.  根据业务场景选择全量解码所有帧，或按索引解码指定帧。
5.  使用完成后释放ImageSource、PixelMap和解码参数等资源。

#### 开发步骤

#### \[h2\]添加链接库

在进行应用开发之前，开发者需要打开native工程的src/main/cpp/CMakeLists.txt，在target\_link\_libraries依赖中添加libimage\_source.so、libpixelmap.so以及日志功能依赖的libhilog\_ndk.z.so。

target\_link\_libraries(entry PUBLIC libhilog\_ndk.z.so libimage\_source.so libpixelmap.so)

#### \[h2\]Native接口调用

具体接口说明请参考[Image\_NativeModule](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-image-nativemodule)。

在DevEco Studio新建Native C++应用，默认生成的项目中包含index.ets文件，在entry\\src\\main\\cpp目录下会自动生成一个cpp文件（hello.cpp或napi\_init.cpp，本示例以hello.cpp文件名为例）。在hello.cpp中实现C API接口调用逻辑，示例代码如下：

**动图解码接口使用示例**

1.  导入相关头文件。
    
    #include <string>
    #include <hilog/log.h>
    #include <multimedia/image\_framework/image/image\_source\_native.h>
    #include <multimedia/image\_framework/image/image\_common.h>
    #include <multimedia/image\_framework/image/pixelmap\_native.h>
    #include "napi/native\_api.h"
    #include <imageKits.h>
    
2.  日志宏定义可参考下述代码按实际需求自行修改。
    
    #undef LOG\_DOMAIN
    #undef LOG\_TAG
    #define LOG\_DOMAIN 0x3200
    #define LOG\_TAG "IMAGE\_SAMPLE"
    
3.  定义动图解码相关类，用于保存ImageSource实例、帧数和解码得到的PixelMap对象。
    
    class ImageAnimatedNative {
    public:
        OH\_ImageSourceNative \*source = nullptr;
        OH\_PixelmapNative \*\*pixelMapList = nullptr;
        OH\_PixelmapNative \*pixelMap = nullptr;
        uint32\_t frameCnt = 0;
        size\_t pixelMapListSize = 0;
        ImageAnimatedNative() {}
        ~ImageAnimatedNative() {}
    };
    
4.  创建ImageAnimatedNative实例。
    
    static ImageAnimatedNative \*g\_thisAnimated = new ImageAnimatedNative();
    
5.  创建GetJsResult函数处理napi返回值。
    
    // 处理napi返回值。
    napi\_value GetJsResult(napi\_env env, int result)
    {
        napi\_value resultNapi = nullptr;
        napi\_create\_int32(env, result, &resultNapi);
        return resultNapi;
    }
    
6.  常量定义。
    
    const int ANIMATED\_MAX\_STRING\_LENGTH = 1024;
    
7.  创建ImageSource实例。
    
    // 创建ImageSource实例。
    napi\_value CreateAnimatedImageSource(napi\_env env, napi\_callback\_info info)
    {
        napi\_value argValue\[1\] = {nullptr};
        size\_t argCount = 1;
        if (napi\_get\_cb\_info(env, info, &argCount, argValue, nullptr, nullptr) != napi\_ok ||
            argCount < 1 || argValue\[0\] == nullptr) {
            OH\_LOG\_ERROR(LOG\_APP, "CreateAnimatedImageSource napi\_get\_cb\_info failed!");
            return GetJsResult(env, IMAGE\_BAD\_PARAMETER);
        }
    
        char filePath\[ANIMATED\_MAX\_STRING\_LENGTH\];
        size\_t pathSize = ANIMATED\_MAX\_STRING\_LENGTH;
        napi\_get\_value\_string\_utf8(env, argValue\[0\], filePath, ANIMATED\_MAX\_STRING\_LENGTH, &pathSize);
    
        if (g\_thisAnimated->source != nullptr) {
            ReleaseAnimatedImageSource(env, info);
        }
    
        Image\_ErrorCode errCode = OH\_ImageSourceNative\_CreateFromUri(filePath, pathSize, &g\_thisAnimated->source);
        return ReturnAnimatedErrorCode(env, errCode, "OH\_ImageSourceNative\_CreateFromUri");
    }
    
8.  获取图片帧数。帧数大于1时，可按动图处理。
    
    // 获取图像帧数。帧数大于1时，可按动图处理。
    napi\_value GetAnimatedFrameCount(napi\_env env, napi\_callback\_info info)
    {
        if (g\_thisAnimated->source == nullptr) {
            OH\_LOG\_ERROR(LOG\_APP, "ImageSource is nullptr.");
            return GetJsResult(env, IMAGE\_BAD\_PARAMETER);
        }
    
        Image\_ErrorCode errCode = OH\_ImageSourceNative\_GetFrameCount(g\_thisAnimated->source, &g\_thisAnimated->frameCnt);
        if (errCode != IMAGE\_SUCCESS) {
            return ReturnAnimatedErrorCode(env, errCode, "OH\_ImageSourceNative\_GetFrameCount");
        }
    
        OH\_LOG\_INFO(LOG\_APP, "Frame count: %{public}u.", g\_thisAnimated->frameCnt);
        return GetJsResult(env, g\_thisAnimated->frameCnt);
    }
    
9.  获取动图帧延迟时间列表。延迟时间列表用于控制每帧显示时长。
    
    // 获取图像延迟时间列表。
    napi\_value GetAnimatedDelayTimeList(napi\_env env, napi\_callback\_info info)
    {
        if (g\_thisAnimated->source == nullptr || g\_thisAnimated->frameCnt == 0) {
            OH\_LOG\_ERROR(LOG\_APP, "Invalid ImageSource or frame count.");
            return GetJsResult(env, IMAGE\_BAD\_PARAMETER);
        }
    
        int32\_t \*delayTimeList = new int32\_t\[g\_thisAnimated->frameCnt\];
        size\_t size = g\_thisAnimated->frameCnt;
        Image\_ErrorCode errCode = OH\_ImageSourceNative\_GetDelayTimeList(g\_thisAnimated->source, delayTimeList, size);
        if (errCode == IMAGE\_SUCCESS) {
            for (size\_t index = 0; index < size; index++) {
                OH\_LOG\_INFO(LOG\_APP, "Frame %{public}zu delay time: %{public}d ms.", index, delayTimeList\[index\]);
            }
        }
        delete\[\] delayTimeList;
        delayTimeList = nullptr;
        return ReturnAnimatedErrorCode(env, errCode, "OH\_ImageSourceNative\_GetDelayTimeList");
    }
    
10.  解码动图帧。
     
     动图帧解码可以根据业务场景选择全量解码或逐帧解码。
     
     *   全量解码：一次性解码所有帧，适用于帧数较少、需要连续播放所有帧的场景。全量解码会同时持有多帧PixelMap，内存占用与帧数和单帧大小相关。
     *   逐帧解码：按索引解码指定帧，适用于需要降低峰值内存占用的场景。逐帧解码时需要保持ImageSource实例有效。
     
     全量解码动图帧示例如下。
     
     // 通过图片解码参数创建PixelMap列表。
     napi\_value CreateAnimatedPixelmapList(napi\_env env, napi\_callback\_info info)
     {
         if (g\_thisAnimated->source == nullptr || g\_thisAnimated->frameCnt == 0) {
             OH\_LOG\_ERROR(LOG\_APP, "Invalid ImageSource or frame count.");
             return GetJsResult(env, IMAGE\_BAD\_PARAMETER);
         }
     
         ReleaseAnimatedPixelmapList();
     
         OH\_DecodingOptions \*opts = nullptr;
         Image\_ErrorCode errCode = OH\_DecodingOptions\_Create(&opts);
         if (errCode != IMAGE\_SUCCESS || opts == nullptr) {
             return ReturnAnimatedErrorCode(env, errCode, "OH\_DecodingOptions\_Create");
         }
     
         g\_thisAnimated->pixelMapList = new OH\_PixelmapNative \*\[g\_thisAnimated->frameCnt\]();
         g\_thisAnimated->pixelMapListSize = g\_thisAnimated->frameCnt;
         errCode = OH\_ImageSourceNative\_CreatePixelmapList(g\_thisAnimated->source, opts,
             g\_thisAnimated->pixelMapList, g\_thisAnimated->pixelMapListSize);
     
         OH\_DecodingOptions\_Release(opts);
         opts = nullptr;
     
         if (errCode != IMAGE\_SUCCESS) {
             ReleaseAnimatedPixelmapList();
             return ReturnAnimatedErrorCode(env, errCode, "OH\_ImageSourceNative\_CreatePixelmapList");
         }
     
         OH\_LOG\_INFO(LOG\_APP, "Create %{public}zu pixelmaps.", g\_thisAnimated->pixelMapListSize);
         return GetJsResult(env, errCode);
     }
     
     按索引解码指定帧示例如下。
     
     // 通过帧索引创建指定帧PixelMap。
     napi\_value CreateAnimatedPixelmapByIndex(napi\_env env, napi\_callback\_info info)
     {
         if (g\_thisAnimated->source == nullptr || g\_thisAnimated->frameCnt == 0) {
             OH\_LOG\_ERROR(LOG\_APP, "Invalid ImageSource or frame count.");
             return GetJsResult(env, IMAGE\_BAD\_PARAMETER);
         }
     
         napi\_value argValue\[1\] = {nullptr};
         size\_t argCount = 1;
         if (napi\_get\_cb\_info(env, info, &argCount, argValue, nullptr, nullptr) != napi\_ok ||
             argCount < 1 || argValue\[0\] == nullptr) {
             OH\_LOG\_ERROR(LOG\_APP, "CreateAnimatedPixelmapByIndex napi\_get\_cb\_info failed!");
             return GetJsResult(env, IMAGE\_BAD\_PARAMETER);
         }
     
         uint32\_t index = 0;
         napi\_get\_value\_uint32(env, argValue\[0\], &index);
         if (index >= g\_thisAnimated->frameCnt) {
             OH\_LOG\_ERROR(LOG\_APP, "Invalid frame index: %{public}u.", index);
             return GetJsResult(env, IMAGE\_BAD\_PARAMETER);
         }
     
         OH\_DecodingOptions \*opts = nullptr;
         Image\_ErrorCode errCode = OH\_DecodingOptions\_Create(&opts);
         if (errCode != IMAGE\_SUCCESS || opts == nullptr) {
             return ReturnAnimatedErrorCode(env, errCode, "OH\_DecodingOptions\_Create");
         }
         OH\_DecodingOptions\_SetIndex(opts, index);
     
         if (g\_thisAnimated->pixelMap != nullptr) {
             OH\_PixelmapNative\_Release(g\_thisAnimated->pixelMap);
             g\_thisAnimated->pixelMap = nullptr;
         }
     
         errCode = OH\_ImageSourceNative\_CreatePixelmap(g\_thisAnimated->source, opts, &g\_thisAnimated->pixelMap);
         OH\_DecodingOptions\_Release(opts);
         opts = nullptr;
     
         return ReturnAnimatedErrorCode(env, errCode, "OH\_ImageSourceNative\_CreatePixelmap");
     }
     
11.  释放资源。
     
     使用完成后，需要释放ImageSource、PixelMap列表、单帧PixelMap等资源。
     
     // 释放动图解码相关资源。
     napi\_value ReleaseAnimatedImageSource(napi\_env env, napi\_callback\_info info)
     {
         ReleaseAnimatedPixelmapList();
     
         if (g\_thisAnimated->pixelMap != nullptr) {
             OH\_PixelmapNative\_Release(g\_thisAnimated->pixelMap);
             g\_thisAnimated->pixelMap = nullptr;
         }
     
         Image\_ErrorCode errCode = IMAGE\_SUCCESS;
         if (g\_thisAnimated->source != nullptr) {
             errCode = OH\_ImageSourceNative\_Release(g\_thisAnimated->source);
             g\_thisAnimated->source = nullptr;
         }
     
         g\_thisAnimated->frameCnt = 0;
         return ReturnAnimatedErrorCode(env, errCode, "OH\_ImageSourceNative\_Release");
     }