文章目录HDR 图到底怎么存的三个核心操作的关系元数据生成代码详解HDR 分解与合成代码详解HdrMetadataType 四种类型对比像素格式与 HDR 类型对应关系StorageLink 串联四个页面的设计思路踩坑记录写在最后一直以来我以为 HDR 图就是更亮的图直到看了HDRImageConversionComponent.ets里的双层分解逻辑才明白 HDR 图的存储结构比想象中复杂。这篇把元数据生成和双层结构都说清楚。HDR 图到底怎么存的用大白话解释 HDR 图像的两种存储方式单层 HDRALTERNATE 类型就是一张高色深的图用 RGBA_1010102每通道 10 bit格式存储整张图包含了完整的 HDR 信息。类比一台原装 OLED 显示器。双层 HDRBASE GAINMAP把 HDR 分成两张图——BASE 层一张普通 SDR 图DISPLAY_P3 色彩空间兼容普通屏幕显示GAINMAP 层一张增益图记录 SDR 和 HDR 之间的亮度差异信息类比BASE 层就是普通照片GAINMAP 层是一张哪里需要亮多少的蒙版。有 HDR 屏幕时两层合成没有就直接用 BASE 层。三个核心操作的关系元数据生成代码详解元数据生成页面MetadataGenerationComponent在onAppear时自动执行依赖前面色彩空间转换页生成的 HDR 图import{image}fromkit.ImageKit;import{colorSpaceManager}fromkit.ArkGraphics2D;importnativePixfromlibentry.so;Componentexportstruct MetadataGenerationComponent{// 从 AppStorage 拿到色彩空间转换页生成的 HDR 图StorageLink(hdrPixelMap)hdrPixelMap:image.PixelMap|undefinedundefined;StatepixelMapDst:image.PixelMap|undefinedundefined;StateisShow:booleanfalse;build(){NavDestination(){// ... UI 代码}.onAppear((){if(!this.hdrPixelMap){return;}this.isShowfalse;// 第一步确保色彩空间是 BT2020_HLGletcolorSpaceBT2020_HLG:colorSpaceManager.ColorSpaceManagercolorSpaceManager.create(colorSpaceManager.ColorSpace.BT2020_HLG);this.hdrPixelMap.setColorSpace(colorSpaceBT2020_HLG);// 第二步设置元数据类型为单层 HDRALTERNATEthis.hdrPixelMap.setMetadata(image.HdrMetadataKey.HDR_METADATA_TYPE,image.HdrMetadataType.ALTERNATE);// 第三步调用 Native 接口生成标准元数据// generateMetadata 会往 PixelMap 里写入 ISO 21496-1 标准的 HDR 元数据nativePix.generateMetadata(this.hdrPixelMap);this.pixelMapDstthis.hdrPixelMap;this.isShowtrue;})}}元数据生成做了什么Native 层的generateMetadata会分析图像的亮度分布计算出 HDRGMHDR Gainmap Metadata等标准元数据写入 PixelMap。这些元数据告诉显示系统这张图的最大亮度是多少、动态范围是多少、用哪个曲线来显示。HDR 分解与合成代码详解HDRImageConversionComponent在onAppear时同时做分解和合成hdrDecomposeAndCompose():void{// 第一步准备两个空白目标 PixelMap // 注意createPixelMap 参数顺序是 (height, width)letdualPixelMap:image.PixelMapnativePix.createPixelMap(this.inputHeight,this.inputWidth);letgainmapPixelMap:image.PixelMapnativePix.createPixelMap(this.inputHeight,this.inputWidth);// 第二步设置 BASE 层和 GAINMAP 层的元数据 // BASE 层SDR 基础层用 DISPLAY_P3 色彩空间letcolorSpaceDisplayP3colorSpaceManager.create(colorSpaceManager.ColorSpace.DISPLAY_P3);dualPixelMap.setColorSpace(colorSpaceDisplayP3);dualPixelMap.setMetadata(image.HdrMetadataKey.HDR_METADATA_TYPE,image.HdrMetadataType.BASE// 标记为 BASE 层);// GAINMAP 层增益图层也用 DISPLAY_P3gainmapPixelMap.setColorSpace(colorSpaceDisplayP3);gainmapPixelMap.setMetadata(image.HdrMetadataKey.HDR_METADATA_TYPE,image.HdrMetadataType.GAINMAP// 标记为 GAINMAP 层);// 第三步分解——单层 HDR 拆成 BASE GAINMAP // hdrPixelMap 是单层 ALTERNATE 类型的 HDR 图nativePix.hdrDecompose(dualPixelMap,gainmapPixelMap,this.hdrPixelMap);this.dualLayerMapdualPixelMap;// 展示 BASE 层this.gainmapPixelMapgainmapPixelMap;// 展示 GAINMAP 层// 第四步合成——BASE GAINMAP 重新合成单层 HDR // 目标格式YCBCR_P010YUV 10bit 格式另一种 HDR 格式letopts:image.InitializationOptions{editable:true,pixelFormat:image.PixelMapFormat.YCBCR_P010,size:{height:this.inputHeight,width:this.inputWidth}};letoutPutPixelMapimage.createPixelMapSync(opts);// 合成结果BT2020_HLG 色彩空间ALTERNATE 类型letcolorSpaceHlgcolorSpaceManager.create(colorSpaceManager.ColorSpace.BT2020_HLG);outPutPixelMap.setColorSpace(colorSpaceHlg);outPutPixelMap.setMetadata(image.HdrMetadataKey.HDR_METADATA_TYPE,image.HdrMetadataType.ALTERNATE);// 把两层合成回单层 HDRnativePix.hdrCompose(dualPixelMap,gainmapPixelMap,outPutPixelMap);this.pixelMapDstoutPutPixelMap;}HdrMetadataType 四种类型对比类型含义对应场景NONE普通 SDR 图无 HDR 元数据普通图片处理ALTERNATE单层 HDR整图就是 HDRAI 生成的 HDR、直接解码的 HDR 图BASE双层 HDR 的基础 SDR 层hdrDecompose 分解出的主图GAINMAP双层 HDR 的增益图层hdrDecompose 分解出的增益信息像素格式与 HDR 类型对应关系这个对应关系弄错了 Native 接口会报错像素格式每通道位深对应 HDR 类型用途RGBA_88888 bitNONESDR普通图片RGBA_101010210 bitALTERNATEAI 生成 HDRYCBCR_P01010 bit YUVALTERNATEhdrCompose 合成结果RGBA_88888 bitBASE/GAINMAP分解后的 BASE 和增益图StorageLink 串联四个页面的设计思路整个 demo 四个页面通过StorageLink(hdrPixelMap)串联// 四个组件都用同一行声明StorageLink(hdrPixelMap)hdrPixelMap:image.PixelMap|undefinedundefined;// 色彩空间转换页写入this.hdrPixelMapoutPutPixelMap;// 自动同步到所有订阅者// 主页读取按钮动态启用Button(元数据生成).enabled(this.hdrPixelMap!undefined)踩坑记录坑1元数据生成必须在 onAppear 里执行generateMetadata是同步操作要确保hdrPixelMap已经存在来自色彩空间转换页。放在构造函数里不行放在aboutToAppear里时机也不对——推荐放在onAppear这时StorageLink数据已经就绪。坑2hdrDecompose 和 hdrCompose 参数顺序// 分解hdrDecompose(base目标, gainmap目标, 源HDR图)nativePix.hdrDecompose(dualPixelMap,gainmapPixelMap,this.hdrPixelMap);// 合成hdrCompose(base层, gainmap层, 合成目标)nativePix.hdrCompose(dualPixelMap,gainmapPixelMap,outPutPixelMap);两个接口的参数看起来像实际含义不同别搞混。坑3hdrCompose 输出用 YCBCR_P010 而不是 RGBA_1010102这两种格式都是 10 bit HDR但用途不同RGBA_1010102是 RGB 色彩空间YCBCR_P010是 YUV 色彩空间更适合视频和合成输出。在这个 demo 里 hdrCompose 的输出用 YCBCR_P010。写在最后搞清楚 HDR 的双层结构后你会明白为什么 HEIC 格式的图片能做到普通屏显示 SDR、HDR 屏显示 HDR——BASE 层保证了兼容性GAINMAP 层提供了增量信息。这套设计挺精妙的。
HDR 图像的双层结构——元数据生成与 hdrDecompose/hdrCompose 完整解析
发布时间:2026/5/21 7:52:06
文章目录HDR 图到底怎么存的三个核心操作的关系元数据生成代码详解HDR 分解与合成代码详解HdrMetadataType 四种类型对比像素格式与 HDR 类型对应关系StorageLink 串联四个页面的设计思路踩坑记录写在最后一直以来我以为 HDR 图就是更亮的图直到看了HDRImageConversionComponent.ets里的双层分解逻辑才明白 HDR 图的存储结构比想象中复杂。这篇把元数据生成和双层结构都说清楚。HDR 图到底怎么存的用大白话解释 HDR 图像的两种存储方式单层 HDRALTERNATE 类型就是一张高色深的图用 RGBA_1010102每通道 10 bit格式存储整张图包含了完整的 HDR 信息。类比一台原装 OLED 显示器。双层 HDRBASE GAINMAP把 HDR 分成两张图——BASE 层一张普通 SDR 图DISPLAY_P3 色彩空间兼容普通屏幕显示GAINMAP 层一张增益图记录 SDR 和 HDR 之间的亮度差异信息类比BASE 层就是普通照片GAINMAP 层是一张哪里需要亮多少的蒙版。有 HDR 屏幕时两层合成没有就直接用 BASE 层。三个核心操作的关系元数据生成代码详解元数据生成页面MetadataGenerationComponent在onAppear时自动执行依赖前面色彩空间转换页生成的 HDR 图import{image}fromkit.ImageKit;import{colorSpaceManager}fromkit.ArkGraphics2D;importnativePixfromlibentry.so;Componentexportstruct MetadataGenerationComponent{// 从 AppStorage 拿到色彩空间转换页生成的 HDR 图StorageLink(hdrPixelMap)hdrPixelMap:image.PixelMap|undefinedundefined;StatepixelMapDst:image.PixelMap|undefinedundefined;StateisShow:booleanfalse;build(){NavDestination(){// ... UI 代码}.onAppear((){if(!this.hdrPixelMap){return;}this.isShowfalse;// 第一步确保色彩空间是 BT2020_HLGletcolorSpaceBT2020_HLG:colorSpaceManager.ColorSpaceManagercolorSpaceManager.create(colorSpaceManager.ColorSpace.BT2020_HLG);this.hdrPixelMap.setColorSpace(colorSpaceBT2020_HLG);// 第二步设置元数据类型为单层 HDRALTERNATEthis.hdrPixelMap.setMetadata(image.HdrMetadataKey.HDR_METADATA_TYPE,image.HdrMetadataType.ALTERNATE);// 第三步调用 Native 接口生成标准元数据// generateMetadata 会往 PixelMap 里写入 ISO 21496-1 标准的 HDR 元数据nativePix.generateMetadata(this.hdrPixelMap);this.pixelMapDstthis.hdrPixelMap;this.isShowtrue;})}}元数据生成做了什么Native 层的generateMetadata会分析图像的亮度分布计算出 HDRGMHDR Gainmap Metadata等标准元数据写入 PixelMap。这些元数据告诉显示系统这张图的最大亮度是多少、动态范围是多少、用哪个曲线来显示。HDR 分解与合成代码详解HDRImageConversionComponent在onAppear时同时做分解和合成hdrDecomposeAndCompose():void{// 第一步准备两个空白目标 PixelMap // 注意createPixelMap 参数顺序是 (height, width)letdualPixelMap:image.PixelMapnativePix.createPixelMap(this.inputHeight,this.inputWidth);letgainmapPixelMap:image.PixelMapnativePix.createPixelMap(this.inputHeight,this.inputWidth);// 第二步设置 BASE 层和 GAINMAP 层的元数据 // BASE 层SDR 基础层用 DISPLAY_P3 色彩空间letcolorSpaceDisplayP3colorSpaceManager.create(colorSpaceManager.ColorSpace.DISPLAY_P3);dualPixelMap.setColorSpace(colorSpaceDisplayP3);dualPixelMap.setMetadata(image.HdrMetadataKey.HDR_METADATA_TYPE,image.HdrMetadataType.BASE// 标记为 BASE 层);// GAINMAP 层增益图层也用 DISPLAY_P3gainmapPixelMap.setColorSpace(colorSpaceDisplayP3);gainmapPixelMap.setMetadata(image.HdrMetadataKey.HDR_METADATA_TYPE,image.HdrMetadataType.GAINMAP// 标记为 GAINMAP 层);// 第三步分解——单层 HDR 拆成 BASE GAINMAP // hdrPixelMap 是单层 ALTERNATE 类型的 HDR 图nativePix.hdrDecompose(dualPixelMap,gainmapPixelMap,this.hdrPixelMap);this.dualLayerMapdualPixelMap;// 展示 BASE 层this.gainmapPixelMapgainmapPixelMap;// 展示 GAINMAP 层// 第四步合成——BASE GAINMAP 重新合成单层 HDR // 目标格式YCBCR_P010YUV 10bit 格式另一种 HDR 格式letopts:image.InitializationOptions{editable:true,pixelFormat:image.PixelMapFormat.YCBCR_P010,size:{height:this.inputHeight,width:this.inputWidth}};letoutPutPixelMapimage.createPixelMapSync(opts);// 合成结果BT2020_HLG 色彩空间ALTERNATE 类型letcolorSpaceHlgcolorSpaceManager.create(colorSpaceManager.ColorSpace.BT2020_HLG);outPutPixelMap.setColorSpace(colorSpaceHlg);outPutPixelMap.setMetadata(image.HdrMetadataKey.HDR_METADATA_TYPE,image.HdrMetadataType.ALTERNATE);// 把两层合成回单层 HDRnativePix.hdrCompose(dualPixelMap,gainmapPixelMap,outPutPixelMap);this.pixelMapDstoutPutPixelMap;}HdrMetadataType 四种类型对比类型含义对应场景NONE普通 SDR 图无 HDR 元数据普通图片处理ALTERNATE单层 HDR整图就是 HDRAI 生成的 HDR、直接解码的 HDR 图BASE双层 HDR 的基础 SDR 层hdrDecompose 分解出的主图GAINMAP双层 HDR 的增益图层hdrDecompose 分解出的增益信息像素格式与 HDR 类型对应关系这个对应关系弄错了 Native 接口会报错像素格式每通道位深对应 HDR 类型用途RGBA_88888 bitNONESDR普通图片RGBA_101010210 bitALTERNATEAI 生成 HDRYCBCR_P01010 bit YUVALTERNATEhdrCompose 合成结果RGBA_88888 bitBASE/GAINMAP分解后的 BASE 和增益图StorageLink 串联四个页面的设计思路整个 demo 四个页面通过StorageLink(hdrPixelMap)串联// 四个组件都用同一行声明StorageLink(hdrPixelMap)hdrPixelMap:image.PixelMap|undefinedundefined;// 色彩空间转换页写入this.hdrPixelMapoutPutPixelMap;// 自动同步到所有订阅者// 主页读取按钮动态启用Button(元数据生成).enabled(this.hdrPixelMap!undefined)踩坑记录坑1元数据生成必须在 onAppear 里执行generateMetadata是同步操作要确保hdrPixelMap已经存在来自色彩空间转换页。放在构造函数里不行放在aboutToAppear里时机也不对——推荐放在onAppear这时StorageLink数据已经就绪。坑2hdrDecompose 和 hdrCompose 参数顺序// 分解hdrDecompose(base目标, gainmap目标, 源HDR图)nativePix.hdrDecompose(dualPixelMap,gainmapPixelMap,this.hdrPixelMap);// 合成hdrCompose(base层, gainmap层, 合成目标)nativePix.hdrCompose(dualPixelMap,gainmapPixelMap,outPutPixelMap);两个接口的参数看起来像实际含义不同别搞混。坑3hdrCompose 输出用 YCBCR_P010 而不是 RGBA_1010102这两种格式都是 10 bit HDR但用途不同RGBA_1010102是 RGB 色彩空间YCBCR_P010是 YUV 色彩空间更适合视频和合成输出。在这个 demo 里 hdrCompose 的输出用 YCBCR_P010。写在最后搞清楚 HDR 的双层结构后你会明白为什么 HEIC 格式的图片能做到普通屏显示 SDR、HDR 屏显示 HDR——BASE 层保证了兼容性GAINMAP 层提供了增量信息。这套设计挺精妙的。