一、写在前面

欢迎加入鸿蒙PC开发者社区,共同打造开发者工具生态:鸿蒙PC开发者社区:https://harmonypc.csdn.net/

项目开源地址:https://atomgit.com/OpenHarmonyPCDeveloper/ohos_krita

欢迎在PC社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper

环境搭建文章:https://blog.csdn.net/weixin_52908342/article/details/161343743

这篇文章记录的是 Krita 在 OpenHarmony / HarmonyOS PC 上的适配过程。

和我之前那些 Electron 项目的鸿蒙适配不一样,Krita 这次走的是一条「硬核」路线。Krita 不是 Web 应用,也不能套个浏览器壳就跑起来——它是一个完整的、桌面级的数字绘画软件,底层是 C++ + Qt + 一整套 KDE Frameworks,还依赖几十个原生库(图像编解码、色彩管理、文字排版、线性代数……)。

所以这次没有捷径:要把 Krita 真正的 C++ 代码,连同它的全部原生依赖,交叉编译成 OHOS arm64,再链接成一个 libentry.so 跑在鸿蒙上。

一句话概括路线:ArkTS 起一个 XComponent → Qt 的 OpenHarmony QPA 插件 dlopen("libentry.so") → 调用导出的 qtmain → 进入 Krita 真实的 main()

整个过程从「依赖一个都编不出来」一路调到「真机上能新建画布、能落笔、能存 .kra」。下面从架构、依赖交叉编译、Krita 本体编译、设备启动调试几个角度,把这次适配完整复盘一遍。

在这里插入图片描述

二、项目背景

Krita 仓库根目录的版本是:

5.4.0-prealpha (Qt5)

它的体量和一个 Web 工具完全不是一个量级:

  • libs/:几十个核心库(kritaimage、kritaui、kritaflake、kritapigment、kritaresources …)
  • plugins/:上百个插件(画笔引擎、滤镜、工具、停靠面板、导入导出)
  • 强依赖 KDE Frameworks (KF5):KConfig / KCoreAddons / KI18n / KWidgetsAddons …
  • 强依赖一大堆原生库:boost、eigen3、lcms2、exiv2、quazip、freetype、harfbuzz、fribidi、libunibreak、xsimd、immer/zug/lager …

这种项目的鸿蒙适配,特点是:

  1. 不能用 Web 套壳,必须真交叉编译 C++。
  2. 依赖是层层嵌套的:Krita 依赖 KF5,KF5 依赖 Qt,大家又都依赖一堆原生库。
  3. 从 macOS 交叉编译到 OHOS,工具链、平台假设、文件系统行为处处是坑。

适配方法上,我借鉴了 tupitube.desk 鸿蒙移植的「壳」思路——也就是上面那条 ArkTS → XComponent → QPA → libentry.so → qtmain 的管线,以及「项目自带一份 Qt for HarmonyOS SDK」的做法。但 Krita 本体的依赖交叉编译、源码适配、运行期方案,全部是为 Krita 重新做的。

一个从一开始就立的原则:Qt 的 SDK 一定要用项目自己的harmony_pc/qtforharmony_sdk/),绝不引用别的项目的 SDK。


三、总体架构

适配后的工程结构(新增 harmony_pc/):

harmony_pc/
├── AppScope/                      鸿蒙应用标识(org.kde.krita / Krita / KDE 图标)
├── entry/
│   ├── src/main/ets/              ArkTS:UIAbility + AbilityStage + XComponent 页面
│   ├── src/main/cpp/              native 入口(CMakeLists)
│   ├── libs/arm64-v8a/            预编译原生载荷(libentry.so + 所有 .so + QPA 插件)
│   └── src/main/resources/rawfile/ Krita 运行期数据(icons/brushes/profiles…)
├── qtforharmony_sdk/              项目自带的 Qt 5.15 for HarmonyOS SDK
└── deps/build-deps.sh            Krita 专属 OHOS 依赖交叉编译框架

运行期管线和标准的「Qt for HarmonyOS」形态一致:

ArkTS UIAbility (EntryAbility.ets)
  └─ 全窗口 XComponent (Index.ets)
       └─ Qt OpenHarmony QPA 插件 (libplugins_platforms_qopenharmony.so)
            └─ dlopen("libentry.so") → 调用导出的 qtmain()
                 └─ Krita 真实 main()(krita/main.cc,Q_OS_OPENHARMONY 守卫)
                      └─ KisApplication / KisMainWindow + 30 个库 + ~150 插件

libentry.so 本身就是 Krita:在 krita/CMakeLists.txt 里,当目标系统是 OHOS 时把 krita 目标编成名为 entry 的共享库;krita/main.cc 里导出 extern "C" qtmain() 别名,指向 Krita 的 main()


四、依赖交叉编译框架

这是整个适配最硬的部分。我写了一个 Krita 专属的依赖交叉编译系统 harmony_pc/deps/build-deps.sh,把 Krita 的全部原生依赖按依赖顺序交叉编译成 OHOS arm64,装进项目本地的 deps/prefix/(和「自带 Qt SDK」一个原则)。

它分三层:

  • tier1 基础 I/O 库:libpng、libjpeg-turbo、libtiff、giflib、libexpat、lcms2
  • tier2 高层依赖:boost、eigen3、exiv2、quazip、freetype、fontconfig、harfbuzz、fribidi、libunibreak、xsimd、immer/zug/lager
  • tier3 KDE Frameworks:ECM + KConfig/KCoreAddons/KGuiAddons/KWidgetsAddons/KItemViews/KI18n/KCompletion

框架本身解决了几个关键工程问题:

  • 三种构建方式:CMake(多数库)、Meson(fontconfig/fribidi——因为 macOS 自带的旧 diff/awk 会让它们的 autotools config.status 出错)、直接编译源文件(giflib、libunibreak 这种小 C 库)。
  • host 工具 bootstrap:交叉编译 KDE Frameworks 时,构建过程要在 macOS 上运行代码生成工具(kconfig_compiler_kf5desktoptojson)。所以用 Homebrew 的 host Qt5 先编出 host 版工具,再在交叉编译 target KF 时指过去。
  • 多镜像下载 + 自动校验架构:每个产物都用 llvm-readelf 验证是 AArch64 才算过。

每一层全绿、产物全是 AArch64 之后,才有资格去碰 Krita 本体。

在这里插入图片描述

五、编译 Krita 本体

依赖齐了之后,让 Krita 自己的 CMake 去 configure + 编译。这一步又给 Krita 的根 CMakeLists.txtkrita/CMakeLists.txt 以及若干源码打了 OHOS 移植补丁(全部带平台守卫,不影响桌面/安卓/Windows 构建):

  • CMakeLists.txt:交叉编译到 OHOS 时跳过 Objective-C / X11 / D-Bus(这些是 macOS/桌面 Linux 才有的)。
  • krita/CMakeLists.txt:OHOS 上把入口编成 libentry.so
  • krita/main.ccqtmain 入口 + 运行期字体/插件/资源路径初始化。
  • libs/resources/KoResourcePaths.cpp:OHOS 的安装前缀。
  • libs/flake/resources/KoFontFamily.cpp:OHOS 上跳过启动期的字体缩略图渲染。
  • libs/global/KisBezierUtils.cpptool_transform2/*gsl_helpers.cpp:补上 GSL 守卫 / 缺失的 include。

最终编译结果:libentry.so(导出 qtmain)+ 30 个 Krita 库 + ~150 个插件,全部是 OHOS arm64。整个 Krita 数字绘画软件,真正编出来了。

在这里插入图片描述

六、问题困难解决

Krita 本体编出来只是上半场。把 HAP 装到真机上点开,是连环踩坑的下半场。这部分是整个适配最有价值的经验。

6.1 困难一:libc++_shared.so 缺失,QPA 插件加载失败

第一次点开直接闪退,报 Cannot read property attachAbilityStage of undefined。往下看 hilog 才发现真正原因:

failed to load libc++_shared.so (needed by libplugins_platforms_qopenharmony.so)

所有 C++ 的 .so(QPA 插件、Qt、KF、Krita)都依赖 OHOS 的 C++ 运行时 libc++_shared.so,而它没被打进 HAP。打进去即可。

6.2 困难二:RPATH 记成了构建时的绝对路径

接着报 libunibreak.so No such file,路径居然是我 Mac 上的构建绝对路径。根因是手编的小库没设 SONAME,链接方就把绝对路径记进了 NEEDED。给 libgif/libintl/libunibreak 加上 -Wl,-soname 重编即可。

紧接着又遇到 lb_prop_bmp: symbol not found——这是我编 libunibreak 时把它的 linebreakdata.c(定义该符号的源文件)误当成「数据文件」排除了。按官方 SOURCES 精确编译解决。

6.3 困难三:沙箱里没有字体,fontconfig 直接崩

过了加载关,Krita 跑到资源注册阶段 SIGSEGV,栈在 KoFontRegistry::facesForCSSValues。原因是 HAP 沙箱里没有 /etc/fonts 配置,fontconfig 找到 0 个字体,返回空指针。

解决:在 main.cc 启动早期写一份 fontconfig 配置,指向设备自带的 /system/fonts(设备上有 257 个字体),并把 HOME/XDG/缓存都设到沙箱可写目录。

6.4 困难四:rawfile 不落盘——OHOS 不把 HAP 资源解压到磁盘

字体好了之后又崩,这次是 Krita 找不到画笔资源包。诊断发现:OHOS 根本不把 HAP 的 rawfile 解压到文件系统,它只能通过资源管理器 API 读取;而 Krita 用的是文件系统路径,于是几个候选路径全是 exists=false

解决:在 EntryAbility.ets 里,首次启动时用 resourceManager 递归把 rawfile/share 解包到沙箱可写目录,再让 Krita 的 KRITA_INSTALL_PREFIX 指过去。

6.5 困难五:QuaZip 被系统 zlib「符号抢占」

数据有了,Krita 开始加载 .bundle 画笔包,又 SIGSEGV,栈在 QuaZip 的 I/O 回调里。根因很隐蔽:OHOS 的系统 zlib(libshared_libz.z.so)罕见地导出了 minizip 符号unzGoToFirstFile 等),运行时把 QuaZip 自带 minizip 的内部调用「符号插入(interposition)」到了系统版本,两者 ioapi ABI 不一致,回调拿到坏指针就崩。

解决:给 QuaZip 链接加 -Wl,-Bsymbolic,让它的内部调用绑定自己的符号,不被系统库抢占。

6.6 困难六:插件路径

Krita 通过插件目录加载它的色彩引擎等插件(找不到 LittleCMS 会直接退出)。把 KRITA_PLUGIN_PATH 指向 HAP 的扁平 native lib 目录(和安卓的布局一致)即可。

把这六个坎一个个填平,Krita 就从「点开闪退」走到了「完整工作区起来、能画、能存」。


七、构建与运行

交叉编译依赖 + Krita(一次性,需要 OHOS 命令行工具 + Homebrew 的 qt@5/meson/gawk):

cd harmony_pc/deps
./build-deps.sh tier1      # 基础 I/O 库
./build-deps.sh tier2      # 高层依赖 + 文字栈
./build-deps.sh tier3      # ECM + host 工具 + KDE Frameworks
./build-deps.sh krita      # configure + 编译 + 安装 Krita 本体
./build-deps.sh package    # 把 libentry.so + 所有 .so + 数据 装进 HAP

构建 HAP:

cd harmony_pc
export DEVECO_SDK_HOME=<command-line-tools>/sdk
<command-line-tools>/bin/ohpm install
<command-line-tools>/bin/hvigorw --mode module -p module=entry@default \
    -p product=default assembleHap --no-daemon

签名产物输出在:

harmony_pc/entry/build/default/outputs/default/entry-default-signed.hap

build-profile.json5 走的是预编译打包(不让 hvigor 重新编译 native),因为 Krita 本体由它自己的 CMake 体系构建,不适合塞进 hvigor 的单 CMakeLists。

在这里插入图片描述

八、真机验证

把签名 HAP 装到鸿蒙设备并启动:

HDC=<command-line-tools>/sdk/default/openharmony/toolchains/hdc
"$HDC" install -r harmony_pc/entry/build/default/outputs/default/entry-default-signed.hap
"$HDC" shell "aa start -a EntryAbility -b org.kde.krita"

实测在 HUAWEI MateBook Pro(HarmonyOS 6.0.0) 上,完整创作流程跑通:

  1. 启动 → 出现 Krita 欢迎页(New Image / Open Image / Recent Images / 社区链接)。
  2. New Image → 弹出新建文档对话框,色彩配置(sRGB-elle…icc)正常加载
  3. Create → 进入完整绘画工作区:画布、工具箱、笔刷预设(缩略图已加载)、图层、颜色选择器。
  4. 在画布上拖拽 → 画出笔触;标题出现 * 表示已修改。
  5. 保存 → 沙箱里生成 .kra 工程文件。

全程无崩溃。一个完整的桌面级数字绘画软件,真正原生跑在了鸿蒙 PC 上。

在这里插入图片描述

九、适配成果

krita-ohos 已经完成:

  • Krita 全部原生依赖交叉编译到 OHOS arm64(图像/IO 库 + 文字栈 + boost/eigen/exiv2/quazip + KDE Frameworks ×7 + immer/zug/lager + xsimd)
  • Krita 本体交叉编译:libentry.so(导出 qtmain)+ 30 个库 + ~150 个插件,全 OHOS arm64
  • ArkTS → XComponent → QPA → libentry → qtmain → Krita main 全链路打通
  • 运行期方案:rawfile 沙箱解包、fontconfig 指向系统字体、插件路径、libc++_shared 打包、QuaZip 符号绑定
  • 真机验证:启动 → 新建 → 绘画 → 保存 全流程跑通

十、结语

krita-ohos 这次适配,本质上是一次桌面级原生 C++/Qt 应用的鸿蒙交叉编译实战

它和 Web 套壳完全不同——没有捷径可走,从工具链三元组、依赖层层交叉编译、KDE Frameworks 的 host 工具 bootstrap,到设备上 libc++_shared、RPATH、字体、rawfile 落盘、QuaZip 符号抢占的连环调试,每一步都得啃下来。

但结果是实打实的:一个有完整画笔引擎、图层、色彩管理、文字排版能力的专业绘画软件,真正原生运行在了鸿蒙 PC 上,能新建、能画、能存。对「鸿蒙能不能承载重型桌面原生应用」这个问题,这是一个肯定的回答。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐