A
Air官方
【Air 系列 · 鸿蒙 PC 适配全记录】35 / 56
■ 应用:AirRT(from RawTherapee)(AirRT)
■ 定位:RawTherapee 移植,RAW 照片处理
■ 上游:RawTherapee | 路线:GTK3+GDK OHOS | 版本:1.1.1
■ 开源: https://gitcode.com/Andy88/Harmony-RawTherapee
■ 市场状态:已上架 AGC 应用页: https://appgallery.huawei.com/app/6917614380960210675
■ 官网(免费下载): https://www.shunidc.cn/
————————————————————
开源鸿蒙 PC 社区: https://harmonypc.csdn.net/
PC 社区项目申请: https://atomgit.com/OpenHarmonyPCDeveloper
适配开源地址:https://gitcode.com/Andy88/Harmony-RawTherapee
环境搭建文章:https://blog.csdn.net/weixin_52908342/article/details/161343743
▍一、为什么要适配 AirRT
RawTherapee 是开源社区里历史最长的 RAW 显影程序之一:以非破坏性编辑为核心,处理档案(pp3)与图像分离,全程在宽色域空间做管线处理,曝光、色彩、降噪、锐化、镜头校正模块的数量与深度都属第一梯队。它的 GUI 用 gtkmm(GTK3 的 C++ 绑定)写成,引擎 rtengine 与界面 rtgui 同属一个 C++ 工程,这在开源 RAW 处理软件里是典型的"GTK 深度用户"形态。
把这样的软件带到鸿蒙 PC,价值有两层:一是给鸿蒙补上一款专业 RAW 显影工具;二是验证一条比 C 语言 GTK 应用更苛刻的移植路线——gtkmm 依赖的是 GTK 绑定的老 ABI 维护线(gtkmm-3.0、glibmm-2.4、sigc++-2.0),与近年栈内常见的 gtkmm-4/glibmm-2.68 新线互不兼容,整条 mm 链必须按老版本组合重新交叉编译。
AirRT 当前版本 1.0.0,包名 com.airrt.app,支持 phone、tablet、2in1。上游为 RawTherapee dev 分支 2026-07-26 快照。工程 PORTING.md 记载:M0–M4 已全链路打通并在 MateBook Pro 2in1 真机验证,涵盖目录树、编辑器与直方图渲染、导入保存链路、系统选择器桥接与全汉化。module.json5 中 compressNativeLibs 显式关闭,这是预编译共享库路线的第一道保险。
▍二、先划清边界:不把编译通过当成适配完成
RawTherapee 在 Linux 桌面上的形态依赖一整套系统设施:发行版仓库提供的 GTK3/gtkmm、系统 gettext、/usr/share 下的主题与镜头数据库、窗口管理器提供的对话框。交叉编译出静态库只是起点,这些设施在鸿蒙沙箱里都不存在,每一项都要找到宿主或替代物。当前工程的边界如下。
层次 传统桌面方案(Linux 版 RawTherapee) 鸿蒙侧当前方案
窗口承载 GTK 顶层窗口 + 系统窗口管理器 XComponent(TEXTURE)+ 自研 OHOS GDK 后端整帧合成
工具包 发行版仓库安装 GTK3 + gtkmm-3.0 GTK 3.24.43(OHOS GDK 后端)+ mm 老线六库随包交叉编译
主标签栏 GTK Notebook 页签 ArkTS 原生标签栏,经 tabs 桥 JSON 回驱
文件选择 GTK FileChooser 对话框 系统 DocumentViewPicker + 沙箱中转的通用文件桥
数据文件 /usr/share/rawtherapee rawfile 携带 airrt-share,首启部署沙箱并 stamp 门控
国际化 系统 gettext + locale 自研 airlibintl 读取随包 zh_CN .mo
引擎形态 cmake 安装到系统前缀的可执行程序 librtengine.a/librtgui_ohos.a 静态链入 libairrt_core.so
边界中最需要说明的是"去上游名"补丁(P-9/P-10/P-11):随包资源删除上游 LICENSE/AUTHORS/splash/logo,运行时渗出点(ICC 默认版权、窗口标题、TIFF Software 标签、主题默认值等)逐处替换,strip_upstream_info.py 对语言与图标元数据做了 660 处字样清洗并以幂等方式可重复执行。发布形态是 AirRT,不是上游的改名单。
▍三、鸿蒙版的整体架构
工程采用 Air 双层架构:ArkTS 壳承担标签栏、splash、系统 Picker 与部署门控;薄 NAPI 桥 librtbridge.so 只链接 ace_napi/native_window/hilog;重型核心 libairrt_core.so 静态链入 rtengine 与 rtgui,作为 GTK 线程宿主运行。
text
ArkTS 壳(entry/src/main/ets)
├── Index.ets 原生标签栏 + 导入/设置动作 + splash 遮罩
├── DialogPage.ets 多实例对话框子窗口(独立 XComponent)
├── FileManager.ets 四模式通用文件桥(打开/保存/目录写/目录读)
└── EntryAbility.ets rawfile→沙箱部署门控、display metrics 注入
▼ NAPI:librtbridge.so(薄壳,dlopen/dlsym)
OnSurfaceCreated:等 rawfile 部署完成 → 按拓扑序 dlopen 依赖(RTLD_GLOBAL)→ dlsym core API
▼
libairrt_core.so(GTK 线程宿主)
├── 环境变量最前置 → airlibintl → loaders.cache 自举
├── gdk_ohos_display_set_native_window 预绑定 → gtk_init → RT 启动
├── libgtk-3 / libgdk-3(OHOS GDK 后端)+ gtkmm-3 与 mm 老线六库
├── rtengine(静态链入)+ 树内 libraw_r.a + fftw3f/lcms2/exiv2/lensfun
└── rtgui(GTK 界面,rtgui/ohos/ohos_host.cc 为鸿蒙宿主桥)
主要目录如下:
text
AirRT/
├── AppScope/ # 应用配置
├── PORTING.md # 移植地图:选型/依赖矩阵/补丁清单/风险登记册
├── entry/src/main/
│ ├── ets/ # Index/DialogPage/FileManager 等
│ ├── cpp/
│ │ ├── airrt_core.cpp # GTK 线程宿主与钩子桥
│ │ ├── napi_init.cpp # 薄 NAPI 桥
│ │ ├── CMakeLists.txt # core 链接清单与 ELF 后处理
│ │ └── libs/arm64-v8a/ # 47 个运行时 .so(GTK3/mm 老线/图像依赖)
│ └── resources/rawfile/airrt-share/ # rtdata、themes、lensfun、locale
├── entry/libs/arm64-v8a/ # 模块级打包通道补充 .so
└── native/
├── cross/ # Meson cross 文件 + CMake 工具链
├── scripts/ # build_mm/build_rt/deploy/elf 规整脚本
├── sources/ # mm 老线源码(gtkmm 3.24.9、glibmm 2.66.9 等)
└── install/ # 交叉编译安装前缀
▍四、核心适配过程
### 1. mm 老 ABI 线六库的平行安装
RawTherapee 的 gtkmm-3.0 要求把整条 C++ 绑定链拉回老 ABI 维护线:sigc++ 2.10.8、cairomm、glibmm 2.66.9(提供 glibmm-2.4/giomm-2.4)、pangomm 2.46.5、atkmm 2.28.5、gtkmm 3.24.9,全部源码收录在 native/sources/,由 native/scripts/build_mm.sh 按依赖顺序交叉编译,与栈内可能并存的新 ABI 线以不同 soname 平行安装。版本组合对齐 Debian trixie 的同款搭配,glibmm 需显式开启废弃 API 才能保留 Glib::Thread。
### 2. RawTherapee 全树静态化与 ohos_host 宿主桥
native/scripts/build_rt.sh 以 OHOS_PLATFORM=ON、BUILD_SHARED=OFF 配置全树,产出 librtengine.a 与 librtgui_ohos.a,连同树内 libraw 静态链入 libairrt_core.so。鸿蒙侧的接入点收敛在新增的 rtgui/ohos/ohos_host.{h,cc}:导出 airt_rt_main、打开文件、标签、偏好设置、导出等 C ABI 函数,树内其余改动为 20 余处 __OHOS__ 守卫。CMake 后处理对 core 执行 NEEDED 去版本号、--remove-needed libc.so、strip 与 64K 对齐规整,这套 ELF 流水线继承自 AirGimp 的 elf_edit.py/fix_elf_align.py。
### 3. 双 C++ ABI 世界的排雷:exiv2 与 lensfun 动态重编
entry/src/main/cpp/CMakeLists.txt 的链接清单里有一条被注释固化的教训:预编译栈中的 exiv2 内嵌静态 libc++,与本进程的动态 libc++_shared.so 形成双运行时,exiv2 抛出的异常基类 typeinfo 与 RT 侧 catch(std::exception&) 判定不相等,异常逃逸直接导致导入图片闪退。工程为此新写 build_exiv2_dyn.sh 与 build_lensfun_dyn.sh,把这两个库以动态 libc++ 方式重编,C++ 异常才能跨越库边界被正确捕获。
### 4. 四模式通用文件桥
GTK FileChooser 在沙箱里没有对应物,工程把它抽象为一次跨语言 RPC:entry/src/main/ets/utils/FileManager.ets 的 handleFileRequest(mode, multi, title, suggested, suffixes, token) 支持四种模式——多选打开、带建议名的保存、目录写(按 CSV 拆分新文件名)、目录读。打开方向把 Picker 选中的 URI 以 4MB 分块拷入 filesDir/tmp/picker_in/ 再把沙箱路径交还 RT;保存方向先建 staging,经 flushOut() 查映射后把 RT 写出的产物写回用户选择的 URI。取消与异常一律回 [],RT 侧对话框语义保持完整。
### 5. 启动时序铁律与原生标签栏
core 线程的启动顺序被固化为不可变序:环境变量最前置(glib 会缓存 home 目录)→ airlibintl → gdk-pixbuf loaders.cache 自举 → gdk_ohos_display_set_native_window 预绑定 NativeWindow → 通知尺寸 → RT 初始化。ArkTS 侧则把 RT 主窗口的 Notebook 标签栏重做为原生组件:Index.ets 经 registerTabsCallback 接收 id/kind/label/closable/current 结构的 JSON,选中与关闭回驱 setTab/closeTab,导入与设置动作固定在标题左侧;splash 遮罩轮询 isLoaded 待首帧后撤层。模态对话框走 DialogPage.ets 的多实例子窗口,每个对话框一个独立 XComponent,由 native 按 id 路由帧与输入。
▍五、适配中遇到的几个难点
### 难点一:僵尸 surface 导致白窗与 CPU 占满
M0 阶段出现"白窗、CPU 96%、DGLES EGL_BAD_SURFACE 刷屏"的组合症状。根因是 ArkUI 会静默重配 texture 队列,渲染线程手里持有的旧 surface 变成僵尸对象,每帧 swap 都在失败重试。解法是把资源所有权交给渲染线程自持:swap 失败即自拆当前 surface,OnSurfaceChanged 触发自愈重建,宿主层不做任何跨线程的资源代管。
### 难点二:触摸与鼠标回调的本机 API 漂移
预想中的 NDK 触摸回调签名与本机 sysroot 头不一致:触摸是主 Callback 成员,鼠标走独立的 RegisterMouseEventCallback,TouchEvent 上没有 windowWidth 与 sourceTool 字段。工程按 sysroot 实际头文件重写注入层,触摸事件统一经 GDK 的 _gdk_windowing_got_event 标准入口进入事件管线——这个入口绕过会直接崩溃,是 OHOS GDK 后端的硬约束。
### 难点三:hvigor 与 ABI 的两道装机门槛
本版 hvigor 的 schema 拒绝 compressNativeLibs 键,工程改为只保留 debugSymbol.strip:false 防 strip 破坏预编译库;2in1 模拟器 CPU 为 x86_64,安装报 9568347 ABI 不匹配,解法是 abiFilters 声明双 ABI 并让 CMake 按 CMAKE_SYSTEM_PROCESSOR 自适应库路径。两道门槛都在 M0 记录于踩坑表,属于打包纪律的一部分。
### 难点四:filesDir 的挂载视图与取证路径
排查时发现 hdc 拉取的 trace 文件"查无",实际是 filesDir 的沙箱挂载视图与物理路径不一致:物理文件位于 /data/app/el2/100/base/<bundle>/haps/entry/files 之下。日志与取证流程因此固定按物理路径取,配合"日志一律写 .txt、仅 ERROR 上 hilog"的铁律,避免 hilog 流控阻塞与安全扫描挂起。
### 难点五:发布形态与上游字样零残留
作为独立发布的 AirRT,上游名称不能从随包资源或运行时字符串里渗出。补丁分三层落地:资源层删 splash/logo/关于信息;运行时渗出点逐处替换(ICC 描述、窗口标题、TIFF 标签、临时目录名);元数据层由 strip_upstream_info.py 幂等清洗 660 处字样。ohos_host.cc 还把进程 arg0 定为 airrt,保证任何崩溃日志里的进程名都是本应用。
▍六、编译、安装与启动
Native 侧按 native/README.md 的顺序执行(工具链与前缀路径以 scripts/env.sh 为准):
bash
source native/scripts/env.sh
native/scripts/download_sources.sh # 获取依赖源码包与 fmt
native/scripts/build_mm.sh # mm 老线六库 + fftw/libiptcdata/fmt
native/scripts/build_exiv2_dyn.sh # exiv2(动态 libc++ 链接)
native/scripts/build_lensfun_dyn.sh # lensfun(动态 libc++ 链接)
native/scripts/build_rt.sh # RawTherapee 全树(librtengine.a/librtgui_ohos.a)
native/scripts/normalize_installs.sh # install 原件 ELF 规整
native/scripts/deploy_stack.sh # 部署依赖栈到应用工程
native/scripts/deploy_datafiles.sh # 部署 rtdata 数据文件
随后构建 HAP:
bash
export JAVA_HOME=/Applications/DevEco-Studio.app/Contents/jbr/Contents/Home
export DEVECO_SDK_HOME=/Applications/DevEco-Studio.app/Contents/sdk
hvigorw --mode module -p module=entry assembleHap --no-daemon
安装并启动:
bash
hdc install -r entry/build/default/outputs/default/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.airrt.app
▍七、当前功能边界
PORTING.md 记载 M0–M4 已全链路打通并经 MateBook Pro 2in1 真机验证,当前已覆盖:
- RT GTK 主窗口在鸿蒙 PC 上运行,目录树、编辑器与直方图渲染正常;
- 原生标签栏桥接:标签数据回驱、选中与关闭、首帧后撤 splash;
- 系统选择器全桥接:导入多选 RAW、保存、目录读写的四模式文件桥;
- 偏好设置弹窗可点开可操作,全中文界面(airlibintl + zh_CN .mo);
- 导入与保存链路端到端可用(含 staging 与 URI 回写映射);
- 图标防闪退处理与 mm 老线全栈随包部署;
- 上游字样零残留的三层清洗(P-9/P-10/P-11)。
尚未覆盖或明确挂起的能力:OpenMP 并行暂关(P-6,待 M3 后实测决定);RT 主题在 OHOS 后端下出现过暗问题(登记为优先遗留);标签栏 TSFN 投递与保存 flush 的 URI 写入仍有遗留项待收口;大图内存峰值(风险登记册 R13,5000 万像素级)未做完整回归,首发采取限制策略;网络更新检查禁用待查(P-7);平板形态回归未完成。里程碑 M5–M7(输入链细化、字体与取证体系完善、性能与批处理回归)尚未宣布完成。
因此,当前版本更准确的定位是"RawTherapee 鸿蒙 PC 基础显影版":主流程——导入、显影、保存——已在真机闭环,性能、并行与长尾交互仍按里程碑推进。
▍八、总结
AirRT 的主要贡献不在单个功能点,而在它把 gtkmm 这条老 ABI 路线完整搬进了鸿蒙沙箱:从 sigc++ 2.10 到 gtkmm 3.24 的六库链、与双 C++ ABI 世界的异常语义冲突、ELF 规整与部署通道,全部有脚本与补丁留痕,后续任何 gtkmm 系 GTK3 应用移植都可以直接引用这套产物与流程。RawTherapee 本体则以极小的侵入(一个 ohos_host 桥加 20 余处守卫)获得了完整的新宿主。
后续重心按里程碑排定:先收口标签栏投递与保存 URI 写入两个遗留项,再解决主题过暗与输入链细化,最后进入 M7 的大图与批处理性能回归。风险登记册里的每一条都对应对策与责任里程碑,这是这类全栈移植能持续收敛的关键。
■ 应用:AirRT(from RawTherapee)(AirRT)
■ 定位:RawTherapee 移植,RAW 照片处理
■ 上游:RawTherapee | 路线:GTK3+GDK OHOS | 版本:1.1.1
■ 开源: https://gitcode.com/Andy88/Harmony-RawTherapee
■ 市场状态:已上架 AGC 应用页: https://appgallery.huawei.com/app/6917614380960210675
■ 官网(免费下载): https://www.shunidc.cn/
————————————————————
开源鸿蒙 PC 社区: https://harmonypc.csdn.net/
PC 社区项目申请: https://atomgit.com/OpenHarmonyPCDeveloper
适配开源地址:https://gitcode.com/Andy88/Harmony-RawTherapee
环境搭建文章:https://blog.csdn.net/weixin_52908342/article/details/161343743
▍一、为什么要适配 AirRT
RawTherapee 是开源社区里历史最长的 RAW 显影程序之一:以非破坏性编辑为核心,处理档案(pp3)与图像分离,全程在宽色域空间做管线处理,曝光、色彩、降噪、锐化、镜头校正模块的数量与深度都属第一梯队。它的 GUI 用 gtkmm(GTK3 的 C++ 绑定)写成,引擎 rtengine 与界面 rtgui 同属一个 C++ 工程,这在开源 RAW 处理软件里是典型的"GTK 深度用户"形态。
把这样的软件带到鸿蒙 PC,价值有两层:一是给鸿蒙补上一款专业 RAW 显影工具;二是验证一条比 C 语言 GTK 应用更苛刻的移植路线——gtkmm 依赖的是 GTK 绑定的老 ABI 维护线(gtkmm-3.0、glibmm-2.4、sigc++-2.0),与近年栈内常见的 gtkmm-4/glibmm-2.68 新线互不兼容,整条 mm 链必须按老版本组合重新交叉编译。
AirRT 当前版本 1.0.0,包名 com.airrt.app,支持 phone、tablet、2in1。上游为 RawTherapee dev 分支 2026-07-26 快照。工程 PORTING.md 记载:M0–M4 已全链路打通并在 MateBook Pro 2in1 真机验证,涵盖目录树、编辑器与直方图渲染、导入保存链路、系统选择器桥接与全汉化。module.json5 中 compressNativeLibs 显式关闭,这是预编译共享库路线的第一道保险。
▍二、先划清边界:不把编译通过当成适配完成
RawTherapee 在 Linux 桌面上的形态依赖一整套系统设施:发行版仓库提供的 GTK3/gtkmm、系统 gettext、/usr/share 下的主题与镜头数据库、窗口管理器提供的对话框。交叉编译出静态库只是起点,这些设施在鸿蒙沙箱里都不存在,每一项都要找到宿主或替代物。当前工程的边界如下。
层次 传统桌面方案(Linux 版 RawTherapee) 鸿蒙侧当前方案
窗口承载 GTK 顶层窗口 + 系统窗口管理器 XComponent(TEXTURE)+ 自研 OHOS GDK 后端整帧合成
工具包 发行版仓库安装 GTK3 + gtkmm-3.0 GTK 3.24.43(OHOS GDK 后端)+ mm 老线六库随包交叉编译
主标签栏 GTK Notebook 页签 ArkTS 原生标签栏,经 tabs 桥 JSON 回驱
文件选择 GTK FileChooser 对话框 系统 DocumentViewPicker + 沙箱中转的通用文件桥
数据文件 /usr/share/rawtherapee rawfile 携带 airrt-share,首启部署沙箱并 stamp 门控
国际化 系统 gettext + locale 自研 airlibintl 读取随包 zh_CN .mo
引擎形态 cmake 安装到系统前缀的可执行程序 librtengine.a/librtgui_ohos.a 静态链入 libairrt_core.so
边界中最需要说明的是"去上游名"补丁(P-9/P-10/P-11):随包资源删除上游 LICENSE/AUTHORS/splash/logo,运行时渗出点(ICC 默认版权、窗口标题、TIFF Software 标签、主题默认值等)逐处替换,strip_upstream_info.py 对语言与图标元数据做了 660 处字样清洗并以幂等方式可重复执行。发布形态是 AirRT,不是上游的改名单。
▍三、鸿蒙版的整体架构
工程采用 Air 双层架构:ArkTS 壳承担标签栏、splash、系统 Picker 与部署门控;薄 NAPI 桥 librtbridge.so 只链接 ace_napi/native_window/hilog;重型核心 libairrt_core.so 静态链入 rtengine 与 rtgui,作为 GTK 线程宿主运行。
text
ArkTS 壳(entry/src/main/ets)
├── Index.ets 原生标签栏 + 导入/设置动作 + splash 遮罩
├── DialogPage.ets 多实例对话框子窗口(独立 XComponent)
├── FileManager.ets 四模式通用文件桥(打开/保存/目录写/目录读)
└── EntryAbility.ets rawfile→沙箱部署门控、display metrics 注入
▼ NAPI:librtbridge.so(薄壳,dlopen/dlsym)
OnSurfaceCreated:等 rawfile 部署完成 → 按拓扑序 dlopen 依赖(RTLD_GLOBAL)→ dlsym core API
▼
libairrt_core.so(GTK 线程宿主)
├── 环境变量最前置 → airlibintl → loaders.cache 自举
├── gdk_ohos_display_set_native_window 预绑定 → gtk_init → RT 启动
├── libgtk-3 / libgdk-3(OHOS GDK 后端)+ gtkmm-3 与 mm 老线六库
├── rtengine(静态链入)+ 树内 libraw_r.a + fftw3f/lcms2/exiv2/lensfun
└── rtgui(GTK 界面,rtgui/ohos/ohos_host.cc 为鸿蒙宿主桥)
主要目录如下:
text
AirRT/
├── AppScope/ # 应用配置
├── PORTING.md # 移植地图:选型/依赖矩阵/补丁清单/风险登记册
├── entry/src/main/
│ ├── ets/ # Index/DialogPage/FileManager 等
│ ├── cpp/
│ │ ├── airrt_core.cpp # GTK 线程宿主与钩子桥
│ │ ├── napi_init.cpp # 薄 NAPI 桥
│ │ ├── CMakeLists.txt # core 链接清单与 ELF 后处理
│ │ └── libs/arm64-v8a/ # 47 个运行时 .so(GTK3/mm 老线/图像依赖)
│ └── resources/rawfile/airrt-share/ # rtdata、themes、lensfun、locale
├── entry/libs/arm64-v8a/ # 模块级打包通道补充 .so
└── native/
├── cross/ # Meson cross 文件 + CMake 工具链
├── scripts/ # build_mm/build_rt/deploy/elf 规整脚本
├── sources/ # mm 老线源码(gtkmm 3.24.9、glibmm 2.66.9 等)
└── install/ # 交叉编译安装前缀
▍四、核心适配过程
### 1. mm 老 ABI 线六库的平行安装
RawTherapee 的 gtkmm-3.0 要求把整条 C++ 绑定链拉回老 ABI 维护线:sigc++ 2.10.8、cairomm、glibmm 2.66.9(提供 glibmm-2.4/giomm-2.4)、pangomm 2.46.5、atkmm 2.28.5、gtkmm 3.24.9,全部源码收录在 native/sources/,由 native/scripts/build_mm.sh 按依赖顺序交叉编译,与栈内可能并存的新 ABI 线以不同 soname 平行安装。版本组合对齐 Debian trixie 的同款搭配,glibmm 需显式开启废弃 API 才能保留 Glib::Thread。
### 2. RawTherapee 全树静态化与 ohos_host 宿主桥
native/scripts/build_rt.sh 以 OHOS_PLATFORM=ON、BUILD_SHARED=OFF 配置全树,产出 librtengine.a 与 librtgui_ohos.a,连同树内 libraw 静态链入 libairrt_core.so。鸿蒙侧的接入点收敛在新增的 rtgui/ohos/ohos_host.{h,cc}:导出 airt_rt_main、打开文件、标签、偏好设置、导出等 C ABI 函数,树内其余改动为 20 余处 __OHOS__ 守卫。CMake 后处理对 core 执行 NEEDED 去版本号、--remove-needed libc.so、strip 与 64K 对齐规整,这套 ELF 流水线继承自 AirGimp 的 elf_edit.py/fix_elf_align.py。
### 3. 双 C++ ABI 世界的排雷:exiv2 与 lensfun 动态重编
entry/src/main/cpp/CMakeLists.txt 的链接清单里有一条被注释固化的教训:预编译栈中的 exiv2 内嵌静态 libc++,与本进程的动态 libc++_shared.so 形成双运行时,exiv2 抛出的异常基类 typeinfo 与 RT 侧 catch(std::exception&) 判定不相等,异常逃逸直接导致导入图片闪退。工程为此新写 build_exiv2_dyn.sh 与 build_lensfun_dyn.sh,把这两个库以动态 libc++ 方式重编,C++ 异常才能跨越库边界被正确捕获。
### 4. 四模式通用文件桥
GTK FileChooser 在沙箱里没有对应物,工程把它抽象为一次跨语言 RPC:entry/src/main/ets/utils/FileManager.ets 的 handleFileRequest(mode, multi, title, suggested, suffixes, token) 支持四种模式——多选打开、带建议名的保存、目录写(按 CSV 拆分新文件名)、目录读。打开方向把 Picker 选中的 URI 以 4MB 分块拷入 filesDir/tmp/picker_in/ 再把沙箱路径交还 RT;保存方向先建 staging,经 flushOut() 查映射后把 RT 写出的产物写回用户选择的 URI。取消与异常一律回 [],RT 侧对话框语义保持完整。
### 5. 启动时序铁律与原生标签栏
core 线程的启动顺序被固化为不可变序:环境变量最前置(glib 会缓存 home 目录)→ airlibintl → gdk-pixbuf loaders.cache 自举 → gdk_ohos_display_set_native_window 预绑定 NativeWindow → 通知尺寸 → RT 初始化。ArkTS 侧则把 RT 主窗口的 Notebook 标签栏重做为原生组件:Index.ets 经 registerTabsCallback 接收 id/kind/label/closable/current 结构的 JSON,选中与关闭回驱 setTab/closeTab,导入与设置动作固定在标题左侧;splash 遮罩轮询 isLoaded 待首帧后撤层。模态对话框走 DialogPage.ets 的多实例子窗口,每个对话框一个独立 XComponent,由 native 按 id 路由帧与输入。
▍五、适配中遇到的几个难点
### 难点一:僵尸 surface 导致白窗与 CPU 占满
M0 阶段出现"白窗、CPU 96%、DGLES EGL_BAD_SURFACE 刷屏"的组合症状。根因是 ArkUI 会静默重配 texture 队列,渲染线程手里持有的旧 surface 变成僵尸对象,每帧 swap 都在失败重试。解法是把资源所有权交给渲染线程自持:swap 失败即自拆当前 surface,OnSurfaceChanged 触发自愈重建,宿主层不做任何跨线程的资源代管。
### 难点二:触摸与鼠标回调的本机 API 漂移
预想中的 NDK 触摸回调签名与本机 sysroot 头不一致:触摸是主 Callback 成员,鼠标走独立的 RegisterMouseEventCallback,TouchEvent 上没有 windowWidth 与 sourceTool 字段。工程按 sysroot 实际头文件重写注入层,触摸事件统一经 GDK 的 _gdk_windowing_got_event 标准入口进入事件管线——这个入口绕过会直接崩溃,是 OHOS GDK 后端的硬约束。
### 难点三:hvigor 与 ABI 的两道装机门槛
本版 hvigor 的 schema 拒绝 compressNativeLibs 键,工程改为只保留 debugSymbol.strip:false 防 strip 破坏预编译库;2in1 模拟器 CPU 为 x86_64,安装报 9568347 ABI 不匹配,解法是 abiFilters 声明双 ABI 并让 CMake 按 CMAKE_SYSTEM_PROCESSOR 自适应库路径。两道门槛都在 M0 记录于踩坑表,属于打包纪律的一部分。
### 难点四:filesDir 的挂载视图与取证路径
排查时发现 hdc 拉取的 trace 文件"查无",实际是 filesDir 的沙箱挂载视图与物理路径不一致:物理文件位于 /data/app/el2/100/base/<bundle>/haps/entry/files 之下。日志与取证流程因此固定按物理路径取,配合"日志一律写 .txt、仅 ERROR 上 hilog"的铁律,避免 hilog 流控阻塞与安全扫描挂起。
### 难点五:发布形态与上游字样零残留
作为独立发布的 AirRT,上游名称不能从随包资源或运行时字符串里渗出。补丁分三层落地:资源层删 splash/logo/关于信息;运行时渗出点逐处替换(ICC 描述、窗口标题、TIFF 标签、临时目录名);元数据层由 strip_upstream_info.py 幂等清洗 660 处字样。ohos_host.cc 还把进程 arg0 定为 airrt,保证任何崩溃日志里的进程名都是本应用。
▍六、编译、安装与启动
Native 侧按 native/README.md 的顺序执行(工具链与前缀路径以 scripts/env.sh 为准):
bash
source native/scripts/env.sh
native/scripts/download_sources.sh # 获取依赖源码包与 fmt
native/scripts/build_mm.sh # mm 老线六库 + fftw/libiptcdata/fmt
native/scripts/build_exiv2_dyn.sh # exiv2(动态 libc++ 链接)
native/scripts/build_lensfun_dyn.sh # lensfun(动态 libc++ 链接)
native/scripts/build_rt.sh # RawTherapee 全树(librtengine.a/librtgui_ohos.a)
native/scripts/normalize_installs.sh # install 原件 ELF 规整
native/scripts/deploy_stack.sh # 部署依赖栈到应用工程
native/scripts/deploy_datafiles.sh # 部署 rtdata 数据文件
随后构建 HAP:
bash
export JAVA_HOME=/Applications/DevEco-Studio.app/Contents/jbr/Contents/Home
export DEVECO_SDK_HOME=/Applications/DevEco-Studio.app/Contents/sdk
hvigorw --mode module -p module=entry assembleHap --no-daemon
安装并启动:
bash
hdc install -r entry/build/default/outputs/default/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.airrt.app
▍七、当前功能边界
PORTING.md 记载 M0–M4 已全链路打通并经 MateBook Pro 2in1 真机验证,当前已覆盖:
- RT GTK 主窗口在鸿蒙 PC 上运行,目录树、编辑器与直方图渲染正常;
- 原生标签栏桥接:标签数据回驱、选中与关闭、首帧后撤 splash;
- 系统选择器全桥接:导入多选 RAW、保存、目录读写的四模式文件桥;
- 偏好设置弹窗可点开可操作,全中文界面(airlibintl + zh_CN .mo);
- 导入与保存链路端到端可用(含 staging 与 URI 回写映射);
- 图标防闪退处理与 mm 老线全栈随包部署;
- 上游字样零残留的三层清洗(P-9/P-10/P-11)。
尚未覆盖或明确挂起的能力:OpenMP 并行暂关(P-6,待 M3 后实测决定);RT 主题在 OHOS 后端下出现过暗问题(登记为优先遗留);标签栏 TSFN 投递与保存 flush 的 URI 写入仍有遗留项待收口;大图内存峰值(风险登记册 R13,5000 万像素级)未做完整回归,首发采取限制策略;网络更新检查禁用待查(P-7);平板形态回归未完成。里程碑 M5–M7(输入链细化、字体与取证体系完善、性能与批处理回归)尚未宣布完成。
因此,当前版本更准确的定位是"RawTherapee 鸿蒙 PC 基础显影版":主流程——导入、显影、保存——已在真机闭环,性能、并行与长尾交互仍按里程碑推进。
▍八、总结
AirRT 的主要贡献不在单个功能点,而在它把 gtkmm 这条老 ABI 路线完整搬进了鸿蒙沙箱:从 sigc++ 2.10 到 gtkmm 3.24 的六库链、与双 C++ ABI 世界的异常语义冲突、ELF 规整与部署通道,全部有脚本与补丁留痕,后续任何 gtkmm 系 GTK3 应用移植都可以直接引用这套产物与流程。RawTherapee 本体则以极小的侵入(一个 ohos_host 桥加 20 余处守卫)获得了完整的新宿主。
后续重心按里程碑排定:先收口标签栏投递与保存 URI 写入两个遗留项,再解决主题过暗与输入链细化,最后进入 M7 的大图与批处理性能回归。风险登记册里的每一条都对应对策与责任里程碑,这是这类全栈移植能持续收敛的关键。
楼主
8 天前