基于 .NET/C#+GTK4 的绘图软件,因鸿蒙禁 JIT 致 .NET 运行时无法落地,改用「ArkWeb 宿主+Web/Canvas2D 前端+C# 核心编 WASM」路线并确立图层缓冲常驻预乘 BGRA 的性能铁律,最终在鸿蒙 PC 跑通画笔、撤销重做、选区与滤镜的图像编辑 MVP。
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 21 天前 | ||
| 21 天前 | ||
| 21 天前 | ||
| 21 天前 | ||
| 21 天前 | ||
| 21 天前 | ||
| 21 天前 | ||
| 21 天前 | ||
| 21 天前 | ||
| 21 天前 | ||
| 21 天前 | ||
| 21 天前 | ||
| 21 天前 | ||
| 21 天前 | ||
| 21 天前 | ||
| 21 天前 | ||
| 21 天前 | ||
| 21 天前 | ||
| 21 天前 | ||
| 21 天前 | ||
| 21 天前 | ||
| 13 天前 | ||
| 21 天前 | ||
| 21 天前 | ||
| 21 天前 | ||
| 21 天前 | ||
| 21 天前 | ||
| 21 天前 | ||
| 21 天前 |
Pinta 的 OpenHarmony 适配记录
这份文档记录我们把 Pinta 搬到 OpenHarmony / HarmonyOS PC 上的过程:原项目是什么、为什么不能照搬原来的代码、最后选了哪条路、鸿蒙工程怎么搭、HAP 怎么编出来跑到真机上,以及现在哪些功能在鸿蒙端能用、哪些是后续要补的。写得比较实,因为这个项目和 Qt / Electron 那类"有现成运行时直接套壳"的移植很不一样——Pinta 是 .NET / C# + GTK4 的桌面绘图软件,鸿蒙上既没有 GTK 也没有能直接跑的 .NET 运行时,所以我们没法"套个壳加载原代码"了事,整条路都得重新趟。
Pinta 原项目(GTK# 版的 Paint.NET 克隆):http://pinta-project.com/ ,源码 https://github.com/PintaProject/Pinta。
先把背景和路线讲清楚
原版 Pinta 的技术栈是 .NET 10 / C#,UI 用 GTK4 + libadwaita,画布和成像走 Cairo。这套东西在 Linux / Windows / macOS 上跑得很好,但到了鸿蒙就遇到两堵墙:
- UI 层(GTK4 + libadwaita,约 90 个文件)在鸿蒙上复用率基本为零——鸿蒙没有 GTK 这一套,菜单、工具栏、图层面板、对话框只能重写。
- 运行时也是个硬约束:HarmonyOS 5.0+ 禁止 JIT / 匿名可执行内存,CoreCLR / Mono 的 JIT 都用不了,.NET 在鸿蒙上理论上只剩 NativeAOT 一条路,而且 GC / seccomp 还有坑;我们也调研过 OpenHarmony.NET / Avalonia 这类"让 .NET 直接上鸿蒙"的方案,但它在 2026-05 已经归档、停在 .NET 9、核心仓 license 都是 null,没法当基座。
所以我们在 harmony-port/README.md 里做了一轮比较完整的方案对比,最后定下来的路线(项目里叫"路线 Y")是:
用 ArkWeb 当宿主,UI 和画布用 Web 技术(HTML/CSS/JS + Canvas2D)重写,C# 核心算法当作"规格"逐步移植 / 将来编成 WASM 复用,系统能力经 ArkWeb ↔ ArkTS 桥接到鸿蒙 API。
选 ArkWeb 的理由很直接:UI 反正怎么都得重写,那就用生态最熟、和 Cairo 直绘对得上的 Web 路线(Cairo 的绘图 API 和 Canvas2D 几乎 1:1);而 ArkWeb 是华为定制的 Chromium(Blink + V8),WASM / Canvas2D / WebGL / JS↔ArkTS 桥都支持,PC 形态还有独立渲染进程,是现成可用的宿主。C# 那几万行算法(Pinta.Core / Pinta.Effects / Pinta.Tools)是 Pinta 的核心资产,留着将来编 WASM 复用,但因为它强依赖 Cairo(每个图层的像素缓冲就是 Cairo.ImageSurface,光直接用到 Cairo 的就有 135 个文件),得先做一层 Cairo 抽象才能编,这块还没动。
需要老实说明的是:当前鸿蒙端的画图能力,是我们用 JS 在 Web 前端里按 Pinta 的算法重新实现的,还不是把原 C# 代码编进去跑。原 C# 工程在这次适配里没有被改动,它在桌面端照旧能 dotnet run。换句话说,这次是"把功能在鸿蒙能用的栈上重做",不是"把原二进制搬过来"。
我们在动手重写之前先验了最致命的那个风险——大图逐像素滤镜在 Web Canvas 上够不够快。结论是够(详见后文"适配里踩过的关键点"),这才放心走 Web 路线。
工程结构 / 鸿蒙工程在哪
鸿蒙相关的全部内容都在仓库根目录下的 harmony-port/(注意是连字符)。这个目录是独立于原 Pinta 源码树的,原项目的 Pinta/ Pinta.Core/ Pinta.Effects/ 等还在根目录原封不动放着。harmony-port/ 内部又分成"Web 前端开发源"和"鸿蒙宿主工程"两块:
ohos_Pinta/
├── Pinta/ Pinta.Core/ Pinta.Effects/ Pinta.Tools/ ... # 原版 .NET/C#+GTK 源码(桌面端,未改动)
├── Pinta.sln # 原桌面解决方案
├── README.md # 原项目说明
└── harmony-port/ # ★ 鸿蒙适配工作目录(全部新增内容在这)
├── README.md # 路线决策 + 进度 + 真机验证记录(第一手资料)
├── deploy.sh # 一键:打包前端 → 构建 HAP → 装机 → 启动
├── sync-webapp.sh # 打包 Web 前端并同步进鸿蒙工程的 rawfile
├── spike-canvas-perf/ # 风险①验证:纯 JS 跑 Pinta 真实算子的性能 spike
├── app/ # ★ Web 前端开发源(纯前端、无构建框架,可 node 测)
│ ├── index.html styles.css
│ ├── package.json # scripts: test / serve
│ ├── build-webapp.mjs # 把 src/ 打成 dist/app.bundle.js(带"漏文件防呆")
│ ├── test/smoke.node.js # node 冒烟测试(像素/合成/滤镜/撤销重做/选区…)
│ └── src/
│ ├── pixels.js # RGBA↔预乘BGRA 转换、预乘 alpha-over
│ ├── document.js # Document / Layer 模型(图层=预乘BGRA缓冲)
│ ├── compositor.js # 多图层预乘空间合成
│ ├── filters.js / adjustments.js / imageops.js # 滤镜 / 调整 / 图像操作
│ ├── tools.js viewport.js canvasview.js history.js
│ ├── platform.js # ★ 平台抽象:有桥走原生、无桥退化浏览器
│ ├── selftest.js # 浏览器内自检
│ └── ui.js / app.js # 工具栏/面板/菜单装配 + 入口
└── ohos-app/ # ★ 鸿蒙宿主工程(DevEco Studio 打开这个目录)
├── build-profile.json5 # 工程级:SDK 版本 / 签名 / 产物
├── AppScope/app.json5 # 应用级:bundleName / 版本 / 图标 / 显示名
├── oh-package.json5 hvigorfile.ts
└── entry/ # 主模块(HAP)
└── src/main/
├── module.json5 # EntryAbility + deviceTypes(含 2in1)
├── ets/
│ ├── entryability/EntryAbility.ets # 加载 pages/Index
│ └── pages/Index.ets # ★ ArkWeb 宿主 + NativeBridge(原生桥)
└── resources/
└── rawfile/webapp/ # ★ 打包后的 Web 前端(部署产物,由 sync 脚本生成)
├── index.html app.bundle.js styles.css
要点说三句:
- 鸿蒙宿主工程是
harmony-port/ohos-app/,用 DevEco Studio 要打开的就是这个目录(不是仓库根,也不是harmony-port/)。 harmony-port/app/是 Web 前端的开发源,可以脱离鸿蒙、直接在浏览器和 node 里开发测试;ohos-app/.../rawfile/webapp/是它打包后的部署产物,由sync-webapp.sh拷进去。改了app/里的前端代码,一定要重新同步一次,rawfile 里才是新的。- 这套设计让同一份前端在浏览器和鸿蒙里都能跑(靠
app/src/platform.js做"有桥走原生、无桥退化浏览器"的抽象),开发时绝大多数事情在浏览器里就能调完,省得每次都去打 HAP。
环境要求
| 项目 | 要求 | 备注 |
|---|---|---|
| DevEco Studio | 装好 HarmonyOS SDK | 命令行部署用到它自带的 hvigorw / hdc / node,路径见 deploy.sh |
| compatibleSdkVersion | 5.0.0(12) |
见 harmony-port/ohos-app/build-profile.json5;DevEco 打开时可能会按你本地 SDK 校正 |
| runtimeOS | HarmonyOS |
同上 |
| 目标设备 | 2in1(鸿蒙 PC)为主,兼容 tablet / phone |
module.json5 里 deviceTypes 三种都声明了,真机验证是在 PC(2in1)上做的 |
| Node.js | 需要 | 用来跑前端打包脚本 build-webapp.mjs 和 node 冒烟测试;deploy.sh 用的是 DevEco 自带的 node |
| Python 3 | 可选 | 只在本地用 python3 -m http.server 起前端调试服务器时用 |
| .NET 10 SDK | 暂不需要 | 只有将来要把 C# 核心编成 WASM 时才需要(含 wasm-tools);当前鸿蒙端不依赖 .NET |
| 签名 | 内置一份本机调试签名 | build-profile.json5 里的 signingConfigs 指向 ~/.ohos/config/ 下的证书,换机器要重新生成 |
应用信息(取自 AppScope/app.json5 与资源文件):
bundleName:com.pinta.ohos- 显示名(
app_name):Pinta versionName:0.1.0,versionCode:1000000vendor:PintaProject
编译与运行
整个流程是「打包前端 → 同步进鸿蒙工程 → 构建 HAP → 装到 2in1 设备」。仓库里 deploy.sh 已经把这几步串成一条命令了,下面先讲手动分步,再讲一键。
1. 拉代码
git clone <仓库地址>
cd ohos_Pinta
2.(建议)先在浏览器里把前端跑一遍
前端是纯 ES 模块、没有构建框架,但因为用了 ES module,得经 http 服务打开,不能 file:// 直接开:
cd harmony-port/app
python3 -m http.server 8000 # 或 npm run serve
# 浏览器打开 http://localhost:8000/
改业务逻辑、调样式建议都先在浏览器里确认,比每次打 HAP 快太多。还可以跑一下 node 冒烟测试:
cd harmony-port/app
npm test # 实际是 node test/smoke.node.js
3. 打包前端并同步进鸿蒙工程
bash harmony-port/sync-webapp.sh
这一步做两件事:先用 node app/build-webapp.mjs 把 app/src/ 那十几个模块按 ORDER 拼成单文件 app.bundle.js、连同 index.html / styles.css 一起生成到 app/dist/,再把 app/dist/ 里的 index.html / app.bundle.js / styles.css 拷到:
harmony-port/ohos-app/entry/src/main/resources/rawfile/webapp/
打包脚本带"漏文件防呆":如果你在
app/src/新增了模块却忘了加进build-webapp.mjs的ORDER,构建会直接失败——这是早期踩过坑加的,漏了的话单文件 bundle 里符号未定义,真机上就是白屏。
4-A. 命令行一键部署(推荐)
装了 DevEco Studio 的机器上,直接:
bash harmony-port/deploy.sh
脚本里依次是:同步前端(调上面的 sync-webapp.sh)→ 用 hvigorw ... assembleHap 构建 HAP → hdc install -r 装到设备 → hdc shell aa start -b com.pinta.ohos -a EntryAbility 启动。它默认从 /Applications/DevEco-Studio.app/Contents 下找 SDK / hvigor / hdc,路径不对的话改脚本顶部那几个变量即可。产物在:
harmony-port/ohos-app/entry/build/default/outputs/default/entry-default-signed.hap
4-B. 用 DevEco Studio 跑
第一次上手或者要调鸿蒙原生侧(Index.ets 的桥)走这条更省心:
- 用 DevEco Studio 打开
harmony-port/ohos-app/(注意是这个子目录); - 第一次先
File → Sync and Refresh Project,再到Project Structure → Signing Configs配好自动签名(真机/模拟器都要); - 选
2in1(PC)设备或模拟器,点 Run; - 改了
app/里的前端后,回仓库根跑一次bash harmony-port/sync-webapp.sh把 rawfile 更新掉,再 Run。
这个项目是如何适配的
和套壳类项目不一样,这里没有"加载原应用"这种事——鸿蒙端的界面和画图是我们按 Pinta 重写 / 重做出来的。逐条说清楚改了什么、为什么这么改。
先验最致命的风险,再决定路线(harmony-port/spike-canvas-perf/)。走 Web 路线最怕的是"逐像素滤镜在 Web Canvas 上太慢、根本没法用",这个要是不成立,整条路就废了。所以我们先写了个纯 JS 的 spike,忠实移植了 Pinta 真实的 Invert、Desaturate 两个算子(连"预乘 BGRA ↔ 非预乘 RGBA"的转换都如实处理),再用可分离 box blur 代表邻域卷积类的重滤镜,实测下来:12MP(4000×3000)大图上 Invert 约 14.5ms、Desaturate 约 15.5ms,纯 JS 基线就够用,WASM 后还更宽裕;box blur 这类重滤镜(99ms)才是瓶颈,留给 WASM/WebGL 解决。正确性自检 4/4 通过。这个风险排除后,Web 路线才算成立。
一条架构铁律:图层常驻预乘 BGRA,转换只做一次(app/src/pixels.js)。spike 里有个意外发现——"RGBA↔预乘BGRA 往返"本身就要 57ms(12MP),比滤镜还贵。所以前端设计上定死了:工作缓冲一直存预乘 BGRA,绝不每帧来回转,转换只在加载和上屏各做一次。document.js 里每个图层的缓冲就是预乘 BGRA,compositor.js 也在预乘空间里合成。
界面用 Web 重做成原版 Pinta 的桌面布局。菜单栏(文件 / 编辑 / 视图 / 图像 / 图层 / 调整 / 效果 / 帮助)+ 工具栏 + 左侧工具箱 + 右侧停靠面板(图层 / 历史 / 调色板)+ 状态栏,尽量贴着原版 Pinta 的样子来,这样老用户上手不别扭。这部分在 ui.js / app.js 里装配。
画布渲染和工具用 JS + Canvas2D 重写。canvasview.js 负责渲染循环和指针输入派发(只有内容变了才重新合成,省开销);tools.js 实现画笔(轨迹插值落墨)、抓手等;viewport.js 管平移缩放的坐标变换。Cairo 那套直绘换成 Canvas2D,因为两边 API 几乎对得上。
滤镜 / 调整按 Pinta 的算法逐个移植(filters.js / adjustments.js / imageops.js)。Invert、Desaturate 是从 Pinta 忠实移植的,box blur 先放了个重算子占位。调整、图像翻转旋转这些也在这几个文件里。
撤销 / 重做用脏矩形快照(history.js)。历史面板 + Ctrl+Z / Ctrl+Y,配合脏矩形快照做局部恢复。
选区(Selection)。矩形选区带蚂蚁线虚线框,画笔 / 橡皮 / 填充 / 滤镜都只作用于选区内;Esc 或"取消选择"清除。
鸿蒙宿主:ArkWeb 加载 rawfile 里的单文件前端(ohos-app/entry/src/main/ets/pages/Index.ets)。用 Web 组件、src 指向 $rawfile('webapp/index.html')。这里有个坑:ES module 在 resource://rawfile 方案下可能被拦,所以我们才在 build-webapp.mjs 里把前端打成单文件经典脚本绕开它。Web 组件上开了 javaScriptAccess / domStorageAccess / fileAccess / imageAccess。
原生桥:registerJavaScriptProxy 把系统能力注入前端(Index.ets 里的 NativeBridge)。在 onControllerAttached 里把 NativeBridge 注册成 window.harmony,暴露两个方法:platform(同步,返回 'harmonyos',给前端探测平台用)和 savePng(异步,注册在异步方法列表里,JS 端拿到的是 Promise)。savePng 的实现是:把前端传来的 data:image/png;base64,... 解码,用 picker.DocumentViewPicker 弹系统文件管理器让用户自选保存位置和文件名,再用 fileIo 写盘;取消返回 'cancelled',出错返回 'error: ...'。
前端侧的平台抽象:同一份前端两处跑(app/src/platform.js)。Platform.isHarmony 靠 window.harmony 是否存在判断;保存 PNG 时,鸿蒙里走 window.harmony.savePng(弹系统文件管理器落盘),浏览器里退化成 <a download> 触发下载。所以前端代码不用写两份,环境差异这一层就吸收掉了。
应用配置。module.json5 里 deviceTypes 声明了 2in1 / tablet / phone,主攻 PC(2in1);入口 EntryAbility 在 onWindowStageCreate 里 loadContent('pages/Index')。目前没申请网络权限——rawfile 是本地加载,用不上;将来如果改成加载远程前端,才需要在 module.json5 加 ohos.permission.INTERNET。图标是 1024×1024 的设计版 logo,换图标后得卸载重装才能刷新系统图标缓存。
功能支持情况
状态只有两个,含义如下:
- 能用:在鸿蒙 PC 真机上实际用过、正常。
- 未适配:桌面端特有、或平台没有对应概念的能力,按平台特点主动换了做法或没做,是取舍不是没做完。
补充一句口径上的不同:这里和套壳项目最大的不同是,鸿蒙端能力是我们用 Web 栈重做的,不是把原 C# 跑起来,所以"原版有、这里还没有"的功能会比套壳类多——这是路线决定的,原 C# 几十个滤镜要一个个移植 / 将来上 WASM。
真机验证依据:
harmony-port/README.md与harmony-port/ohos-app/README.md里的真机记录。这两份 README 按截图文件名(pinta-on-device.jpeg/pinta-v2-*.jpeg/pinta-v3-*.jpeg等)引用了验证截图,但截图本身没有随仓库提交,当前树里找不到这些图片文件。下面凡写"真机验过"的,都对应 README 里的文字记录。
T0 基础能力(装得上、起得来、画得出)
这一档是底线:装不上、起不来、一打开白屏,后面都白搭。这一层在真机上全部跑通。
| 能力 | 状态 | 说明 |
|---|---|---|
| 编译打包成 HAP | 能用 | deploy.sh(hvigorw assembleHap)或 DevEco 都能构建,产物 entry-default-signed.hap |
| 安装 / 启动 / 不白屏 | 能用 | 真机装好能起、进主界面;前端打成单文件经典脚本规避了 rawfile 下 ES module 被拦的白屏问题 |
| ArkWeb 加载前端、界面渲染 | 能用 | Pinta 风格的桌面布局(菜单 + 工具栏 + 工具箱 + 停靠面板 + 状态栏)真机完整渲染 |
原生桥生效(window.harmony) |
能用 | 真机状态栏显示"平台 harmonyos (ArkWeb)",registerJavaScriptProxy 注入成功 |
| 画笔交互可绘 | 能用 | 真机上画得出来;指针输入派发正常 |
小结:T0 这层在 2026-06-11 真机上过了——能构建签名、能装能起、不白屏、桥生效、能画。Web 路线在鸿蒙 PC 上能跑这件事,这次验证到了。
T1 主要能力(一个画图软件日常要用的)
平时真正会用的就是这一档:工具、图层、调整、效果、撤销重做、选区、存盘。这些都在真机上实测可用。
| 能力 | 状态 | 说明 |
|---|---|---|
| 画笔 / 铅笔 / 橡皮擦 / 填充 / 吸管 | 能用 | 真机实测可用,指针输入派发正常 |
| 矩形选区(蚂蚁线) | 能用 | 真机拖拽成框验过;画笔/橡皮/填充/滤镜均只作用选区内 |
| 选区约束填充 / 选区约束滤镜 | 能用 | 真机验过(选区内 invert 仅处理选区,2.9–4.4ms) |
| 缩放 / 抓手(平移缩放) | 能用 | 真机实测可用,viewport.js 坐标变换 |
| 图层:增 / 复制 / 删 / 上下移 / 显隐 / 选择 | 能用 | 真机实测可用,node 冒烟测试同样通过 |
| 图层:不透明度 / 混合模式(正常·正片叠底·滤色) | 能用 | 真机实测可用(预乘空间合成) |
| 调整:亮度·对比度(对话框实时预览) | 能用 | 真机实测可用 |
| 调整:自动色阶 / 黑白 / 反色 / 色调分离 / 棕褐 | 能用 | 真机实测可用(反色滤镜 8.6ms) |
| 效果:模糊(Box Blur) | 能用 | 真机可用,纯 JS 下偏慢(重算子),后续靠 WASM/WebGL 提速 |
| 图像:水平/垂直翻转、旋转 90°/180°、拼合、裁剪到选区 | 能用 | 真机实测可用,node 测同样通过 |
| 编辑:全选 / 取消选择 / 填充选区 / 擦除选区 | 能用 | 真机实测可用 |
| 撤销 / 重做(历史面板,Ctrl+Z/Y) | 能用 | 真机验过逐笔撤销(只撤最后一笔);脏矩形快照 |
| 调色板 | 能用 | 真机实测可用 |
| 打开图片 / 导出 PNG | 能用 | 导出 PNG 经原生桥 savePng 弹系统文件管理器自选位置落盘,真机验过 |
小结:T1 这层"能画、能分层、能调、能撤、能存"的核心闭环已经在鸿蒙 PC 真机上跑通,逐项实测可用。node test/smoke.node.js 23/23 通过。Box Blur 功能本身可用,只是纯 JS 实现下偏慢,后续上 WASM/WebGL 提速。
T2 增强 / 平台受限能力(原版的加分项 / 还在路上的部分)
这一档是原版 Pinta 有、但这次路线下还没来得及做、或者得靠后续技术手段才划算做的部分。
| 能力 | 状态 | 说明 |
|---|---|---|
| 原生"保存为可再编辑工程格式" | 未适配 | 导出 PNG 的 picker 落盘已通、真机验过(见 T1);工程格式保存是 README 里列的下一步,当前实现只到 PNG |
| 其余 30+ 滤镜 / 效果 | 未适配 | 原 C# Pinta.Effects 里还有几十个效果没移植;按算法逐个重写 / 将来上 WASM 复用 |
| 形状 / 文字 / 渐变 / 魔棒等工具 | 未适配 | 这阶段没实现,属于后续工具扩展,不放假按钮 |
| 更多可配置的调整 | 未适配 | 同上,先把核心几个做扎实 |
| C# 核心编成 WASM 复用 | 未适配 | 被 Cairo 耦合阻挡,需先做 Cairo 抽象层 + 装 .NET 10 SDK / wasm-tools,属于后续进阶工作 |
| OffscreenCanvas → Worker → WebGL、8K 大画布内存 | 未适配 | 重滤镜上 WASM/WebGL 时才需要的进阶能力,本阶段未实现 |
| 网络权限相关能力 | 未适配 | 当前 rawfile 本地加载不需要联网,故未申请 ohos.permission.INTERNET |
小结:T2 这层大部分标"后续",原因是路线决定的——原版几十个滤镜和高级工具是 C# 写的,要么按算法重写、要么等 C# 核心编成 WASM 才能整批复用,而 WASM 这条还卡在"得先抽掉 Cairo 耦合"上,属于后续进阶工作。这些是"还在路上",不是"按平台取舍掉了"——和套壳项目里"托盘/菜单栏按平台习惯不做"性质不同,得区分开。
这么算下来,整体到什么程度
把三档合起来看:
- T0 基础能力:构建签名、装机启动、不白屏、ArkWeb 加载、原生桥、画笔可绘,真机全部验过、可以正常使用。这是底线,已经到位。
- T1 主要能力:核心画图闭环(工具 / 图层 / 调整 / 效果 / 撤销重做 / 选区 / 导出 PNG)已在鸿蒙 PC 真机上逐项实测可用,node 冒烟测试 23/23 过。Box Blur 功能本身可用,只是纯 JS 实现下偏慢。
- T2 增强能力:导出 PNG 的原生 picker 已通、真机验过;其余几十个滤镜、形状/文字/渐变等工具、以及 C# 核心编 WASM 都还在后续清单里,这是路线和阶段决定的,属于"还在路上"。
一句话总结:这次不是把 Pinta 原样搬上鸿蒙——GTK 和 .NET 运行时这两堵墙决定了那条路走不通。我们选的是 ArkWeb + Web 前端重写、C# 核心当规格逐步移植 / 留给 WASM 的路线,先把最致命的 Web 渲染性能风险验掉,再把一个有图层、有选区、有撤销重做、能导出 PNG 的画图 MVP 在鸿蒙 PC 真机上跑通、实测可用。剩下的是把原版那几十个滤镜和高级工具一项项补齐,并把 C# 核心编成 WASM 复用。
适配的第一手细节、方案对比、真机验证记录都在
harmony-port/README.md和harmony-port/ohos-app/README.md里(这两份 README 按文件名引用了验证截图,但截图本身未随仓库提交)。