HarmonyOS 图片缩放没想象中简单——detailEnhance 四档质量深度解析
文章目录先搞清楚为什么不直接用系统缩放整体数据流核心代码拆解第一步创建源 PixelMap 和目标 PixelMap第二步用 Select 控件选择档位并触发增强第三步Native 层接口声明Index.d.ts四档质量对比踩坑总结StorageLink 实现跨页面状态共享写在最后用 ImageKit 处理图片缩放第一次用的时候我以为就是个 resize结果发现里面有个detailEnhance的参数四个挡位差异挺大的。这篇文章就把这块说透。先搞清楚为什么不直接用系统缩放普通的scale()或者直接改 PixelMap 尺寸底层用的是最近邻或双线性插值放大之后会明显模糊、出锯齿。detailEnhance是走了 C Native 层的图像增强算法相当于给缩放结果做了一轮超分辨率处理。用大白话说NONE直接缩放最快质量最差LOW轻度增强比 NONE 稍好一点速度也快MEDIUM中等增强日常使用最均衡的选择HIGH最强增强细节最好耗时最长不适合实时处理整体数据流核心代码拆解第一步创建源 PixelMap 和目标 PixelMapimport{image}fromkit.ImageKit;importnativePixfromlibentry.so;import{hilog}fromkit.PerformanceAnalysisKit;getPixMap():void{try{// 从 rawfile 创建 ImageSourceletimageSource:image.ImageSourceimage.createImageSource(this.getUIContext().getHostContext()?.resourceManager.getRawFdSync(ic_scaling.png));// 同步解码为 PixelMapthis.pixelMapSrcimageSource.createPixelMapSync();// 获取原始尺寸constimageInfo:image.ImageInfothis.pixelMapSrc.getImageInfoSync();this.inputWidthimageInfo.size.width;this.inputHeightimageInfo.size.height;// 创建目标 PixelMap放大 1.5 倍constopts:image.InitializationOptions{editable:true,pixelFormat:image.PixelMapFormat.RGBA_8888,size:{height:imageInfo.size.height*this.zoomRatio,// zoomRatio 1.5width:imageInfo.size.width*this.zoomRatio}};this.pixelMapDstimage.createPixelMapSync(opts);}catch(e){hilog.error(0x0000,ImageScaling,getPixMap error${JSON.stringify(e)});}}注意目标 PixelMap 是个空白画布真正的像素写入是由 Native 层的detailEnhance完成的。不要以为createPixelMapSync(opts)就直接缩放好了。第二步用 Select 控件选择档位并触发增强Componentexportstruct ImageScalingComponent{Stateindex:number0;// 对应 NONE0, LOW1, MEDIUM2, HIGH3StatepixelMapSrc:image.PixelMap|undefinedundefined;StatepixelMapDst:image.PixelMap|undefinedundefined;StateinputWidth:number0;StateinputHeight:number0;privatezoomRatio:number1.5;build(){Column(){// 展示原图Image($rawfile(ic_scaling.png)).width(100%).aspectRatio(1.25)// 选择增强档位Select([{value:NONE},{value:LOW},{value:MEDIUM},{value:HIGH}]).selected(this.index).onSelect((index:number,text:string){this.indexindex;// 每次选择都重新创建目标 PixelMapthis.pixelMapDstundefined;this.getPixMap();// 调用 Native 增强接口// 第五个参数 index: 0NONE, 1LOW, 2MEDIUM, 3HIGHnativePix.detailEnhance(this.pixelMapSrc,this.pixelMapDst,this.inputWidth,this.inputHeight,this.index);this.isShowtrue;})// 展示增强后的结果if(this.isShow){Image(this.pixelMapDst).width(100%).aspectRatio(1.25)}}}}第三步Native 层接口声明Index.d.ts// libentry.so 对应的 TypeScript 类型声明exportconstdetailEnhance:(src:image.PixelMap|undefined,dst:image.PixelMap|undefined,width:number,height:number,quality:number// 0:NONE 1:LOW 2:MEDIUM 3:HIGH)void;exportconstcreatePixelMap:(height:number,width:number)image.PixelMap;四档质量对比档位对应 index处理速度细节保留适用场景NONE0最快最差放大后模糊明显缩略图预览LOW1快略好于 NONE列表图片MEDIUM2中等均衡细节较好日常图片展示HIGH3慢最佳细节锐利打印、精细展示踩坑总结坑1目标 PixelMap 必须每次重新创建每次切换档位时pixelMapDst undefined然后重新getPixMap()是必要的。如果复用旧的 PixelMap上次的像素数据会残留Native 层写入可能出现异常。坑2pixelFormat 必须是 RGBA_8888创建目标 PixelMap 时pixelFormat要指定RGBA_8888不能省略。如果用UNKNOWN或者其他格式Native 层写入会失败并返回错误码。坑3宽高顺序注意detailEnhance接口的参数是(src, dst, inputWidth, inputHeight, quality)但createPixelMap的参数顺序是(height, width)。这两个顺序不一样写错了图会出现拉伸变形。坑4SELECT 选同一项不触发 onSelectSelect 组件如果选的是当前已选项onSelect不会触发。如果需要强制刷新要额外加一个按钮来重新处理。StorageLink 实现跨页面状态共享项目里用了一个很精妙的设计色彩空间转换页生成 HDR 图后元数据生成页和 HDR 层转换页才会激活。这是用StorageLinkAppStorage做的。// 主页Index.etsStorageLink(hdrPixelMap)hdrPixelMap:image.PixelMap|undefinedundefined;// 按钮动态启用/禁用HDR 图存在才能点Button(元数据生成).enabled(this.hdrPixelMap!undefined)// 色彩空间转换页生成 HDR 图后if(imageInfo.isHdr){this.hdrPixelMapthis.pixelMapSrc;// AppStorage 自动同步}不需要回调不需要事件总线状态一改所有绑定了StorageLink(hdrPixelMap)的组件都会自动更新。写在最后图片缩放不是把尺寸改了就完事算法档位的选择对用户体验影响很大。MEDIUM 是大多数场景的最优解HIGH 留给需要打印或精细展示的场景。另外要记住Native 层接口是同步调用HIGH 档处理大图时会卡住 UI 线程生产环境记得放到 TaskPool 里跑。