使用Image_NativeModule解码HDR图片,获取解码结果的动态范围信息,并根据业务需要处理HDR图片、SDR图片和带有GainMap辅助图的Picture对象。
HDR(High Dynamic Range,高动态范围)图片相比SDR(Standard Dynamic Range,标准动态范围)图片,可以记录更丰富的亮度层次和色彩信息。应用在解码HDR图片时,需要关注图片源是否包含HDR信息、设备是否支持HDR解码、解码结果是否仍为HDR,以及后续显示、编辑、编码或分享场景是否需要回退为SDR。当前系统仅支持JPEG格式和HEIF格式的HDR图片,且在接口调用方未主动配置相关参数的情况下,默认使用SDR处理。
HDR图片解码主要包含以下流程:
在进行HDR图片开发前,建议先了解以下概念。
| 概念 | 说明 |
|---|---|
| SDR | 标准动态范围图像,适用于普通显示、编辑和分享场景。 |
| HDR | 高动态范围图像,可表达更高亮度范围和更丰富的明暗层次,显示效果依赖图片数据、设备能力和显示链路。 |
| 动态范围 | 表示图像亮度信息的表达范围。解码时可通过动态范围参数指定期望输出HDR或SDR。 |
| GainMap | 一种HDR兼容方案,图片中包含SDR主图和表示亮度增益信息的辅助图。支持HDR的设备可结合主图和GainMap生成HDR效果;不支持HDR时可使用SDR主图进行兼容显示。 |
| Picture | 多图对象,可承载主图和辅助图。对于带GainMap的图片,可通过Picture相关接口获取辅助图或合成HDR PixelMap。 |
HDR解码结果受图片格式、图片元数据、设备能力、解码参数和内存类型等因素影响。源图为HDR图片时,解码结果不一定始终为HDR;当设备或参数不满足条件时,可能回退为SDR结果。
在进行应用开发之前,开发者需要打开native工程的src/main/cpp/CMakeLists.txt,在target_link_libraries依赖中添加libimage_source.so、libpixelmap.so、libpicture.so、libimage_packer.so以及日志功能依赖的libhilog_ndk.z.so。
target_link_libraries(entry PUBLIC libhilog_ndk.z.so libimage_source.so libpixelmap.so libpicture.so libimage_packer.so)
具体接口说明请参考Image_NativeModule。
在DevEco Studio新建Native C++应用,默认生成的项目中包含index.ets文件,在entry\src\main\cpp目录下会自动生成一个cpp文件(hello.cpp或napi_init.cpp,本示例以hello.cpp文件名为例)。在hello.cpp中实现C API接口调用逻辑,示例代码如下:
导入相关头文件。
#include <cstring> #include <string> #include <hilog/log.h> #include <multimedia/image_framework/image/image_common.h> #include <multimedia/image_framework/image/image_source_native.h> #include <multimedia/image_framework/image/pixelmap_native.h> #include <multimedia/image_framework/image/picture_native.h> #include <multimedia/image_framework/image/image_packer_native.h> #include "napi/native_api.h" #include <imageKits.h>
日志宏定义可参考下述代码按实际需求自行修改。
#undef LOG_DOMAIN #undef LOG_TAG #define LOG_DOMAIN 0x3200 #define LOG_TAG "IMAGE_SAMPLE"
定义HDR图片解码相关类,用于保存ImageSource、PixelMap、Picture和解码结果信息。
class ImageHdrColorSpaceNative {
public:
OH_ImageSourceNative *source = nullptr;
OH_PixelmapNative *pixelMap = nullptr;
OH_PixelmapNative *mainPixelMap = nullptr;
OH_PixelmapNative *gainmapPixelMap = nullptr;
OH_PixelmapNative *hdrPixelMap = nullptr;
OH_Pixelmap_ImageInfo *pixelMapImageInfo = nullptr;
OH_PictureNative *picture = nullptr;
bool isHdr = false;
ImageHdrColorSpaceNative() {}
~ImageHdrColorSpaceNative() {}
}; 创建ImageHdrColorSpaceNative实例。
static ImageHdrColorSpaceNative *g_hdrColorSpace = new ImageHdrColorSpaceNative();
创建GetJsResult函数处理napi返回值。
// 处理napi返回值。
napi_value GetJsResult(napi_env env, int result)
{
napi_value resultNapi = nullptr;
napi_create_int32(env, result, &resultNapi);
return resultNapi;
} 常量定义。
const int MAX_STRING_LENGTH = 1024;
创建ImageSource实例。
ImageSource用于管理图片源数据。后续HDR解码、Picture解码和图片信息获取均基于ImageSource完成。
napi_value CreateHdrImageSource(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, "CreateHdrImageSource napi_get_cb_info failed!");
return GetJsResult(env, IMAGE_BAD_PARAMETER);
}
if (g_hdrColorSpace->source != nullptr) {
ReleaseHdrColorSpaceSource(env, info);
}
char filePath[MAX_STRING_LENGTH];
size_t pathSize = MAX_STRING_LENGTH;
napi_get_value_string_utf8(env, argValue[0], filePath, MAX_STRING_LENGTH, &pathSize);
Image_ErrorCode errCode = OH_ImageSourceNative_CreateFromUri(filePath, pathSize, &g_hdrColorSpace->source);
return ReturnHdrErrorCode(env, errCode, "OH_ImageSourceNative_CreateFromUri");
} 设置动态范围并解码为PixelMap。
使用OH_DecodingOptions_SetDesiredDynamicRange设置期望的解码动态范围。常用动态范围策略如下:
| 动态范围策略 | 说明 |
|---|---|
| IMAGE_DYNAMIC_RANGE_AUTO | 根据图片源和设备能力自适应解码。源图为HDR且设备支持时,解码结果可能为HDR;否则可能输出SDR。 |
| IMAGE_DYNAMIC_RANGE_HDR | 期望输出HDR结果。实际结果仍受图片源、设备能力和解码条件影响。 |
| IMAGE_DYNAMIC_RANGE_SDR | 期望输出SDR结果,适用于普通显示、编辑、分享或兼容性优先的场景。 |
napi_value DecodeHdrPixelMap(napi_env env, napi_callback_info info)
{
if (g_hdrColorSpace->source == nullptr) {
OH_LOG_ERROR(LOG_APP, "ImageSource is nullptr.");
return GetJsResult(env, IMAGE_BAD_PARAMETER);
}
OH_DecodingOptions *opts = nullptr;
Image_ErrorCode errCode = OH_DecodingOptions_Create(&opts);
if (errCode != IMAGE_SUCCESS || opts == nullptr) {
return ReturnHdrErrorCode(env, errCode, "OH_DecodingOptions_Create");
}
OH_DecodingOptions_SetDesiredDynamicRange(opts, IMAGE_DYNAMIC_RANGE_AUTO);
ReleasePixelMap(&g_hdrColorSpace->pixelMap);
errCode = OH_ImageSourceNative_CreatePixelmap(g_hdrColorSpace->source, opts, &g_hdrColorSpace->pixelMap);
OH_DecodingOptions_Release(opts);
opts = nullptr;
return ReturnHdrErrorCode(env, errCode, "OH_ImageSourceNative_CreatePixelmap");
} 判断PixelMap解码结果是否为HDR。
解码完成后,可以通过OH_PixelmapNative_GetImageInfo获取PixelMap图像信息,再通过OH_PixelmapImageInfo_GetDynamicRange判断解码结果是否为HDR。该结果表示当前PixelMap的动态范围,不等同于源图片文件一定是HDR或SDR。
napi_value CheckHdrDynamicRange(napi_env env, napi_callback_info info)
{
if (g_hdrColorSpace->pixelMap == nullptr) {
OH_LOG_ERROR(LOG_APP, "PixelMap is nullptr.");
return GetJsResult(env, IMAGE_BAD_PARAMETER);
}
Image_ErrorCode errCode = OH_PixelmapImageInfo_Create(&g_hdrColorSpace->pixelMapImageInfo);
if (errCode != IMAGE_SUCCESS || g_hdrColorSpace->pixelMapImageInfo == nullptr) {
return ReturnHdrErrorCode(env, errCode, "OH_PixelmapImageInfo_Create");
}
errCode = OH_PixelmapNative_GetImageInfo(g_hdrColorSpace->pixelMap, g_hdrColorSpace->pixelMapImageInfo);
if (errCode != IMAGE_SUCCESS) {
OH_PixelmapImageInfo_Release(g_hdrColorSpace->pixelMapImageInfo);
g_hdrColorSpace->pixelMapImageInfo = nullptr;
return ReturnHdrErrorCode(env, errCode, "OH_PixelmapNative_GetImageInfo");
}
bool isHdr = false;
errCode = OH_PixelmapImageInfo_GetDynamicRange(g_hdrColorSpace->pixelMapImageInfo, &isHdr);
if (errCode == IMAGE_SUCCESS) {
g_hdrColorSpace->isHdr = isHdr;
OH_LOG_INFO(LOG_APP, "PixelMap dynamic range is %{public}s.", isHdr ? "HDR" : "SDR");
}
OH_PixelmapImageInfo_Release(g_hdrColorSpace->pixelMapImageInfo);
g_hdrColorSpace->pixelMapImageInfo = nullptr;
return ReturnHdrErrorCode(env, errCode, "OH_PixelmapImageInfo_GetDynamicRange");
} 以SDR方式解码图片。
当应用需要兼容普通显示、编辑、分享或不支持HDR的场景时,可以将期望动态范围设置为IMAGE_DYNAMIC_RANGE_SDR。该方式可作为HDR解码失败或设备不支持HDR时的降级处理。
napi_value DecodeSdrPixelMap(napi_env env, napi_callback_info info)
{
if (g_hdrColorSpace->source == nullptr) {
OH_LOG_ERROR(LOG_APP, "ImageSource is nullptr.");
return GetJsResult(env, IMAGE_BAD_PARAMETER);
}
OH_DecodingOptions *opts = nullptr;
Image_ErrorCode errCode = OH_DecodingOptions_Create(&opts);
if (errCode != IMAGE_SUCCESS || opts == nullptr) {
return ReturnHdrErrorCode(env, errCode, "OH_DecodingOptions_Create");
}
OH_DecodingOptions_SetDesiredDynamicRange(opts, IMAGE_DYNAMIC_RANGE_SDR);
ReleasePixelMap(&g_hdrColorSpace->pixelMap);
errCode = OH_ImageSourceNative_CreatePixelmap(g_hdrColorSpace->source, opts, &g_hdrColorSpace->pixelMap);
OH_DecodingOptions_Release(opts);
opts = nullptr;
return ReturnHdrErrorCode(env, errCode, "OH_ImageSourceNative_CreatePixelmap");
} 解码为Picture。
Picture是Image Kit中用于承载主图和辅助图的图像结构。对于带GainMap的HDR图片,Picture可以同时提供SDR主图、GainMap增益图和合成后的HDR图,方便应用根据设备能力和业务场景选择不同处理路径:
napi_value DecodeHdrPicture(napi_env env, napi_callback_info info)
{
if (g_hdrColorSpace->source == nullptr) {
OH_LOG_ERROR(LOG_APP, "ImageSource is nullptr.");
return GetJsResult(env, IMAGE_BAD_PARAMETER);
}
OH_DecodingOptionsForPicture *options = nullptr;
Image_ErrorCode errCode = OH_DecodingOptionsForPicture_Create(&options);
if (errCode != IMAGE_SUCCESS || options == nullptr) {
return ReturnHdrErrorCode(env, errCode, "OH_DecodingOptionsForPicture_Create");
}
Image_AuxiliaryPictureType desiredAuxiliaryPictures[] = { AUXILIARY_PICTURE_TYPE_GAINMAP };
errCode = OH_DecodingOptionsForPicture_SetDesiredAuxiliaryPictures(options, desiredAuxiliaryPictures, 1);
if (errCode != IMAGE_SUCCESS) {
OH_DecodingOptionsForPicture_Release(options);
options = nullptr;
return ReturnHdrErrorCode(env, errCode, "OH_DecodingOptionsForPicture_SetDesiredAuxiliaryPictures");
}
if (g_hdrColorSpace->picture != nullptr) {
OH_PictureNative_Release(g_hdrColorSpace->picture);
g_hdrColorSpace->picture = nullptr;
}
errCode = OH_ImageSourceNative_CreatePicture(g_hdrColorSpace->source, options, &g_hdrColorSpace->picture);
OH_DecodingOptionsForPicture_Release(options);
options = nullptr;
return ReturnHdrErrorCode(env, errCode, "OH_ImageSourceNative_CreatePicture");
} 从Picture获取SDR主图。
SDR主图可用于普通显示、编辑、分享或HDR处理失败时的回退显示。对于带GainMap的HDR图片,主图通常是兼容SDR显示链路的图像数据。
napi_value GetMainPixelmapFromPicture(napi_env env, napi_callback_info info)
{
if (g_hdrColorSpace->picture == nullptr) {
OH_LOG_ERROR(LOG_APP, "Picture is nullptr.");
return GetJsResult(env, IMAGE_BAD_PARAMETER);
}
ReleasePixelMap(&g_hdrColorSpace->mainPixelMap);
Image_ErrorCode errCode = OH_PictureNative_GetMainPixelmap(g_hdrColorSpace->picture,
&g_hdrColorSpace->mainPixelMap);
return ReturnHdrErrorCode(env, errCode, "OH_PictureNative_GetMainPixelmap");
} 从Picture获取GainMap增益图。
GainMap记录用于从SDR主图还原或增强HDR效果的增益信息。应用如果需要自定义HDR处理,可获取GainMap PixelMap进行后续处理。
napi_value GetGainmapPixelmapFromPicture(napi_env env, napi_callback_info info)
{
if (g_hdrColorSpace->picture == nullptr) {
OH_LOG_ERROR(LOG_APP, "Picture is nullptr.");
return GetJsResult(env, IMAGE_BAD_PARAMETER);
}
ReleasePixelMap(&g_hdrColorSpace->gainmapPixelMap);
Image_ErrorCode errCode = OH_PictureNative_GetGainmapPixelmap(g_hdrColorSpace->picture,
&g_hdrColorSpace->gainmapPixelMap);
return ReturnHdrErrorCode(env, errCode, "OH_PictureNative_GetGainmapPixelmap");
} 从Picture获取HDR合成图。
当Picture中包含有效GainMap时,可直接获取合成后的HDR PixelMap。若Picture中不包含GainMap,接口可能返回IMAGE_UNSUPPORTED_OPERATION,应用可回退到SDR主图。
napi_value GetHdrComposedPixelmapFromPicture(napi_env env, napi_callback_info info)
{
if (g_hdrColorSpace->picture == nullptr) {
OH_LOG_ERROR(LOG_APP, "Picture is nullptr.");
return GetJsResult(env, IMAGE_BAD_PARAMETER);
}
ReleasePixelMap(&g_hdrColorSpace->hdrPixelMap);
Image_ErrorCode errCode = OH_PictureNative_GetHdrComposedPixelmap(g_hdrColorSpace->picture,
&g_hdrColorSpace->hdrPixelMap);
return ReturnHdrErrorCode(env, errCode, "OH_PictureNative_GetHdrComposedPixelmap");
} 对HDR PixelMap进行编码。
如果应用需要将处理后的HDR PixelMap保存为文件,可通过ImagePacker设置编码参数。编码结果是否为HDR与源PixelMap动态范围、编码格式、设备能力和编码参数有关。若业务仅要求兼容分享或普通显示,可优先编码为SDR结果。
napi_value PackHdrPixelMap(napi_env env, napi_callback_info info)
{
if (g_hdrColorSpace->pixelMap == nullptr) {
OH_LOG_ERROR(LOG_APP, "PixelMap is nullptr.");
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, "PackHdrPixelMap napi_get_cb_info failed!");
return GetJsResult(env, IMAGE_BAD_PARAMETER);
}
int32_t fd = -1;
napi_get_value_int32(env, argValue[0], &fd);
OH_ImagePackerNative *packer = nullptr;
Image_ErrorCode errCode = OH_ImagePackerNative_Create(&packer);
if (errCode != IMAGE_SUCCESS || packer == nullptr) {
return ReturnHdrErrorCode(env, errCode, "OH_ImagePackerNative_Create");
}
OH_PackingOptions *packingOptions = nullptr;
errCode = OH_PackingOptions_Create(&packingOptions);
if (errCode != IMAGE_SUCCESS || packingOptions == nullptr) {
OH_ImagePackerNative_Release(packer);
packer = nullptr;
return ReturnHdrErrorCode(env, errCode, "OH_PackingOptions_Create");
}
char type[] = "image/jpeg";
Image_MimeType mimeType = {type, strlen(type)};
OH_PackingOptions_SetMimeType(packingOptions, &mimeType);
// 设置编码质量。quality默认值为0,建议不低于80;本示例统一设置为90,兼顾图片质量和文件体积。
uint32_t quality = 90;
OH_PackingOptions_SetQuality(packingOptions, quality);
OH_PackingOptions_SetDesiredDynamicRange(packingOptions, IMAGE_PACKER_DYNAMIC_RANGE_AUTO);
errCode = OH_ImagePackerNative_PackToFileFromPixelmap(packer, packingOptions, g_hdrColorSpace->pixelMap, fd);
OH_PackingOptions_Release(packingOptions);
packingOptions = nullptr;
OH_ImagePackerNative_Release(packer);
packer = nullptr;
return ReturnHdrErrorCode(env, errCode, "OH_ImagePackerNative_PackToFileFromPixelmap");
} 释放资源。
使用完成后,需要释放ImageSource、PixelMap、Picture等资源,避免内存泄漏。
napi_value ReleaseHdrColorSpaceSource(napi_env env, napi_callback_info info)
{
Image_ErrorCode errCode = IMAGE_SUCCESS;
ReleasePixelMap(&g_hdrColorSpace->hdrPixelMap);
ReleasePixelMap(&g_hdrColorSpace->gainmapPixelMap);
ReleasePixelMap(&g_hdrColorSpace->mainPixelMap);
if (g_hdrColorSpace->picture != nullptr) {
errCode = OH_PictureNative_Release(g_hdrColorSpace->picture);
g_hdrColorSpace->picture = nullptr;
}
ReleasePixelMap(&g_hdrColorSpace->pixelMap);
if (g_hdrColorSpace->source != nullptr) {
errCode = OH_ImageSourceNative_Release(g_hdrColorSpace->source);
g_hdrColorSpace->source = nullptr;
}
g_hdrColorSpace->isHdr = false;
return ReturnHdrErrorCode(env, errCode, "ReleaseHdrColorSpaceSource");
}