鸿蒙PC适配:Pinta GTK 图像编辑器鸿蒙 PC ArkWeb 适配全记录:从 .NET_GTK4 桌面到 HarmonyOS PC HAP
一、写在前面欢迎加入鸿蒙 PC 开发者社区共同打造开发者工具生态鸿蒙 PC 开发者社区https://harmonypc.csdn.net/项目开源地址https://atomgit.com/OpenHarmonyPCDeveloper/ohos_Pinta欢迎在PC社区平台申请新建项目https://atomgit.com/OpenHarmonyPCDeveloper这篇文章记录的是Pinta这个 .NET/GTK 桌面位图编辑器适配 HarmonyOS PC 的过程。Pinta 是一个开源的位图绘图/图像编辑软件定位类似 Paint.NET画笔、橡皮、填充、选区、图层、调整、滤镜是日常修图和涂画的轻量工具。它的技术栈很桌面 Linux用C#.NET 10写界面是GTK4 libadwaita通过 GirCore 绑定成像引擎是Cairo插件系统是 Mono.Addins。整个工程有四百多个.cs文件UI 和算法分布在Pinta、Pinta.Core、Pinta.Tools、Pinta.Effects等多个类库里。这类项目适配鸿蒙很有代表性也很硬它既不是 Electron也不是现代 Web 工程而是一个深度依赖 GTK4 桌面体系 Cairo 原生成像 .NET 运行时的程序。直接把这套搬到鸿蒙端几乎是不可能的——后面会讲为什么。所以这次没有选择硬迁 GTK而是采用更能落地的路线把 Pinta 的核心体验用 Web 重新实现再用 HarmonyOS ArkWeb 包一层形成可以构建 HAP 的鸿蒙 PC 应用C# 原算法只作为规格说明参考逐个忠实移植到 JS。最终适配完成后项目新增了一个零构建的 Web 前端和一个 HarmonyOS ArkWeb 工程。Web 侧负责画布渲染、图层、工具、调整、滤镜、撤销重做、选区鸿蒙侧负责 HAP 工程承载、本地资源加载、文件保存系统文件管理器和平台桥接。二、原项目分析一个深度依赖 GTK4 Cairo 的 .NET 桌面应用适配前先把原项目摸清楚。Pinta 是个多类库的 .NET 解决方案Pinta/ ├── Pinta/ 主程序入口GTK 窗口、菜单、Action ├── Pinta.Core/ 核心文档/图层模型、算法、文件格式、历史记录 ├── Pinta.Tools/ 工具画笔、选区、形状、文字、油漆桶…… ├── Pinta.Effects/ 调整(Adjustments) 效果(Effects) ├── Pinta.Gui.Widgets/ GTK 控件 ├── Pinta.Docking/ 停靠面板 ├── Directory.Build.props TargetFramework net10.0 └── Directory.Packages.props GirCore.Gtk-4.0 / Adw-1 / PangoCairo几个关键事实决定了适配难度语言/运行时C# / .NET 10。UIGTK4 libadwaita通过 GirCore 绑定约 90 个 widget/dialog 文件。成像引擎Cairo而且是深度耦合——每个图层的像素缓冲本身就是Cairo.ImageSurfacePinta.Core/Classes/UserLayer.csCore/Tools/Effects 里有上百个文件直接用 Cairo 画。像素语义Cairo 的 ARGB32 在内存里是预乘 alpha BGRA 字节序。这点后面移植时是个坑。工具约 20 个调整 9 个效果 38 个完整的文件/编辑/视图/图像/图层/调整/效果菜单体系。也就是说Pinta 不是纯算法 少量 UI而是GTK 界面、Cairo 成像、C# 业务三者紧密耦合的桌面程序。三、适配路线判断为什么不能直接搬 GTK选 Web 化 ArkWebPinta 这种项目理论上有三条路线。第一条直接把 GTK4 .NET 搬到鸿蒙。这条路基本是死路。经过对鸿蒙生态的调研GTK4 / Pango / libadwaita 在 OpenHarmony没有任何移植而 .NET 跑鸿蒙的社区方案NativeAOT 那条线当时已归档停更、停在 .NET 9、且键鼠/窗口等基础能力未完成。换句话说鸿蒙上既没有 GTK也没有能跑桌面 GTK 的 .NET 运行时。谁要说能赶紧直接把 GTK Pinta 搬上鸿蒙那才是不现实的。第二条用 ArkTS ArkUI 完整原生重写。长期看最原生但第一版工作量巨大画布、图层合成、几十个滤镜、选区、撤销重做都要在 ArkUI 里重来。第三条Web 化 ArkWeb 包一层。这次选的就是它。选它的原因很直接Pinta 的成像核心是 Cairo而Cairo 的 API 与 HTML5 Canvas 2D 几乎一一对应都是 path/fill/stroke ImageSurface 模型换到 Web 渲染是所有方向里最顺的ArkWeb 本质是华为定制的 ChromiumBlink V8WebAssembly、Canvas2D、WebGL、JS↔ArkTS 桥都支持能直接加载 HAP 内置 rawfile 资源Web 静态页面可以先在普通浏览器里调试反馈快文件保存这类系统能力用 ArkTS 桥接补齐即可。所以本次目标不是逐行迁 C#而是把 Pinta 的 C# 算法当作规格说明在 Web 前端里忠实重新实现保留 Pinta 的产品体验。落地前还做了一个关键的性能预研spike用纯 JS 实现 Pinta 的 Invert / Desaturate 算子对 12MP4000×3000大图测耗时——结果约 15ms完全够用重滤镜如模糊约 100ms后续可上 WASM/WebGL。这一步排除了Web 画布扛不住大图这个最致命的风险路线才算定下来。四、第一步用 Web 重新实现核心忠实于 Cairo 的预乘 BGRAWeb 前端是零构建的纯静态资源模块化组织harmony-port/app/ ├── index.html / styles.css └── src/ ├── pixels.js RGBA(非预乘) ↔ 预乘 BGRA 转换、alpha-over ├── document.js Document / Layer 模型图层预乘BGRA缓冲 ├── compositor.js 多图层合成含混合模式 ├── filters.js Invert/Desaturate(忠实移植) BoxBlur ├── adjustments.js 自动色阶/黑白/亮度对比度/色调分离/棕褐 ├── imageops.js 翻转/旋转/拼合/裁剪到选区 ├── tools.js 画笔/铅笔/橡皮/填充/吸管/选区/缩放/抓手 ├── history.js 撤销重做脏矩形快照 ├── canvasview.js 渲染循环 指针输入 ├── viewport.js 平移/缩放 ├── platform.js 平台抽象浏览器/ArkWeb 双跑 └── ui.js / app.js 菜单/面板装配 入口这里有一个贯穿始终的架构决定图层缓冲常驻预乘 BGRA跟 Pinta/Cairo 内存格式保持一致只在加载图片和上屏时各转一次。spike 实测发现「RGBA ↔ 预乘 BGRA 往返」本身就要 ~57ms比滤镜还贵所以绝不能每帧转换。滤镜也是照 Pinta 的算子忠实移植例如 Invert 在预乘空间下就是A - 通道、Desaturate 用(7471·B 38470·G 19595·R) 16这套亮度权重。为保证移植正确核心逻辑都写了 node 自检像素往返可逆、预乘合成出紫色、滤镜数值、选区内外、翻转旋转、撤销重做……全部通过后再往鸿蒙搬。五、第二步打包成单文件规避 rawfile 下的 ES 模块限制开发时用 ES 模块import/export很舒服但 ArkWeb 从rawfile加载页面时模块脚本的来源 schemeresource://rawfile可能像file://一样拦住script typemodule的 import 链。所以写了一个零依赖打包器build-webapp.mjs把src/*.js按依赖顺序拼成一个传统 IIFE 脚本app.bundle.js去掉import/export再配一个index.html用经典script引入。这样 ArkWeb 加载最稳。这里踩过一个真实的坑新增模块history.js忘了加进打包清单结果单文件 bundle 里符号未定义、真机白屏表现是底部平台显示—、所有动态逻辑不跑。后来给打包器加了漏文件防呆——src/下任何.js没列进清单就直接构建失败。教训单文件打包一定要有漏文件校验。六、第三步新增 HarmonyOS ArkWeb 工程Web 前端稳定后新增了 HarmonyOS 工程Stage 模型harmony-port/ohos-app/ ├── AppScope/app.json5 ├── build-profile.json5 ├── entry/ │ ├── build-profile.json5 / hvigorfile.ts / oh-package.json5 │ └── src/main/ │ ├── module.json5 deviceTypes: [2in1, tablet, phone] │ ├── ets/ │ │ ├── entryability/EntryAbility.ets │ │ └── pages/Index.ets ★ ArkWeb 宿主 原生桥 │ └── resources/ │ ├── base/... 图标/字符串/颜色/页面表 │ └── rawfile/webapp/ ★ 打包后的前端(index.html app.bundle.js styles.css)ArkWeb 加载的资源放在entry/src/main/resources/rawfile/webapp/入口地址是$rawfile(webapp/index.html)。module.json5的deviceTypes带上了2in1鸿蒙 PC/笔记本形态。Index.ets的核心加载逻辑Web({src:$rawfile(webapp/index.html),controller:this.controller}).javaScriptAccess(true).domStorageAccess(true).fileAccess(true).imageAccess(true).onControllerAttached((){// 在页面加载前注册原生桥this.controller.registerJavaScriptProxy(this.bridge,harmony,[platform],[savePng])}).width(100%).height(100%)七、第四步原生桥——点保存弹出系统文件管理器只让页面打开还不够。图像编辑器的保存是核心闭环而且用户希望能自己选保存位置不是悄悄存进沙箱。所以原生桥NativeBridge暴露给 Web 的对象名是harmony注册时把同步/异步能力分开platform()同步让前端探测自己跑在浏览器还是 ArkWebsavePng(dataUrl, fileName)异步JS 端拿到的是 Promise用picker.DocumentViewPicker弹出系统文件管理器让用户选位置/文件名再把 PNG 字节写入。asyncsavePng(dataUrl:string,fileName:string):Promisestring{constb64dataUrl.substring(dataUrl.indexOf(,)1);constbytesnewutil.Base64Helper().decodeSync(b64);constoptionsnewpicker.DocumentSaveOptions();options.newFileNames[fileName];options.fileSuffixChoices[PNG 图片|.png];consturisawaitnewpicker.DocumentViewPicker(this.context).save(options);if(!uris||uris.length0)returncancelled;constfilefs.openSync(uris[0],fs.OpenMode.READ_WRITE|fs.OpenMode.CREATE);fs.writeSync(file.fd,bytes.buffer);fs.closeSync(file);returnuris[0];}前端platform.js做了降级在 ArkWeb 里走window.harmony.savePng返回 Promise在普通浏览器里退化为Blob a[download]下载。所以同一份前端浏览器/鸿蒙双跑。边界很清楚Web 负责画布与算法ArkTS 负责系统文件管理器两边只传一个 dataURL 字符串。八、第五步构建 HAP 并安装真机DevEco Studio 自带 hvigorw 和 hdc可以全命令行完成「打包前端 → 构建 HAP → 安装 → 启动」。本机封装成了一个deploy.sh# deploy.sh 关键步骤bashharmony-port/sync-webapp.sh# 打包前端并同步进 rawfile/webappDEVECO/Applications/DevEco-Studio.app/ContentsDEVECO_SDK_HOME$DEVECO/sdk\$DEVECO/tools/hvigor/bin/hvigorw\--modemodule-pproductdefault assembleHap --no-daemon# 构建 HAPHDC$DEVECO_SDK_HOME/default/openharmony/toolchains/hdc$HDCinstall-r.../entry-default-signed.hap# 安装$HDCshell aa start-bcom.pinta.ohos-aEntryAbility# 启动构建输出在entry/build/default/outputs/default/生成entry-default-signed.hap。构建过程可能有getContext废弃、targetSdkVersion建议等警告不影响本次 MVP 的 HAP 产物正式上架前再补齐签名、清理 API 警告即可。九、把界面做成原版 Pinta 的样子并补上应用图标第一版 UI 是个极简骨架后来按原版 Pinta 的真实菜单/工具/布局把界面重做成了桌面图像编辑器的样子顶部菜单栏文件/编辑/视图/图像/图层/调整/效果/帮助 工具栏 左侧竖排工具箱 右侧停靠面板图层 / 历史记录 / 调色板 状态栏。注意原则是不放点了没反应的假按钮上去的菜单项都接了真功能。应用图标一开始是个占位的纯色方块在桌面/标题栏上看像没 logo。后来用脚本生成了一个 1024×1024 的设计版图标深色渐变底 红绿蓝三色叠加的绘图意象full-bleed 由系统做圆角遮罩。换图标后要卸载重装才能刷新系统图标缓存之后标题栏、任务栏、应用网格都会显示新 logo。十、适配成果与当前边界这次适配已经完成的内容分析原 .NET/GTK4/Cairo 项目结构与核心功能性能 spike 验证 Web 画布可行用 Web 忠实重新实现核心画笔/铅笔/橡皮/填充/吸管/矩形选区/缩放/抓手图层增/复制/删/上下移/显隐 不透明度 混合模式调整自动色阶/黑白/亮度对比度/反色/色调分离/棕褐 效果模糊图像操作翻转/旋转/拼合/裁剪到选区 选区约束的画笔/填充/滤镜撤销重做脏矩形快照 历史面板打包为单文件、新增 ArkWeb 工程、registerJavaScriptProxy原生桥保存走系统文件管理器DocumentViewPicker全命令行构建 HAP 并安装真机验证。当前边界也很清楚原版 38 个效果只实现了少数常用的其余按每个上去的都真能用逐步补形状/文字/渐变/魔棒等工具尚未实现打开图片目前走 Web 的文件选择还没接系统文件管理器重滤镜还是纯 JS后续可上 WASM/WebGL这是 MVP 与可迭代版本正式上架前还要完善签名、权限说明和更多真机测试。这正是 Web 化 MVP 路线的特点先把核心体验跑通再逐步增强。十一、这次适配的几个经验第一GTK/.NET 桌面应用不要硬迁 UI。鸿蒙没有 GTK、也没有可用的桌面 .NET 运行时纠结怎么搬 GTK 不如把产品核心动作抽出来用 Web 重做。第二算法可以照着 C# 当规格重写。Pinta 的滤镜是纯像素数学照源码忠实移植到 JS 即可而且要尊重原始像素语义Cairo 是预乘 BGRA否则颜色就错了。第三一个架构铁律工作缓冲常驻预乘 BGRA只在加载/上屏各转一次。RGBA↔预乘往返比滤镜本身还贵每帧转换会卡。第四ArkWeb 包 rawfile 静态页面打成单文件经典脚本最稳能规避 ES 模块在本地 scheme 下被拦的问题并且单文件打包一定要有漏文件校验。第五文件能力交给 ArkTS 原生桥。保存用DocumentViewPicker让用户选位置体验和系统其他应用一致桥接接口要小Web 与 ArkTS 边界清晰。第六换应用图标后记得卸载重装刷新系统图标缓存否则看着还是旧图标/空白。十二、总结Pinta 这次适配走的是一条适合重型桌面 GUI 应用的路线.NET/GTK4/Cairo 原项目分析 ↓ 判断 GTK 不可直迁 → 选 Web 化 ArkWeb ↓ spike 验证 Web 画布性能 ↓ 用 Web 忠实重实现核心预乘 BGRA / 图层 / 工具 / 调整 / 选区 / 撤销 ↓ 打包单文件 → ArkWeb 加载 rawfile ↓ ArkTS bridge 补齐保存系统文件管理器 ↓ 构建 HarmonyOS PC HAP 并真机验证它没有追求一步复刻所有 GTK 细节而是先把最重要的画图 图层 调整 选区 保存链路做成鸿蒙 PC 上可运行、可构建、可继续迭代的版本。对于 Pinta 这类深度依赖 GTK/Cairo/.NET 的桌面图像编辑器Web 化 ArkWeb 包壳是一个非常适合首版 MVP 的方案它绕开了 GTK 和 .NET 运行时这两道现实的墙能快速验证产品体验也为后续补全工具/效果、原生化和正式发布留下了清晰空间。到这里Pinta 从一个 .NET/GTK4 桌面图像编辑器已经完成了到 HarmonyOS PC ArkWeb HAP 的第一版适配。