比较提交
| 作者 | SHA1 | 提交日期 | |
|---|---|---|---|
|
|
3c7261c3a8 | ||
|
|
5ac0e5833c | ||
|
|
696d5f39b3 | ||
|
|
906f770805 | ||
|
|
fcfd8b0ddc | ||
|
|
e9fddda5e1 | ||
|
|
a2e33e8e00 | ||
|
|
3f075922cb | ||
|
|
183bf0e4b2 | ||
|
|
2d3edcf0a2 | ||
|
|
53ce88dd15 | ||
|
|
3bb33818ea | ||
|
|
80f22e2285 | ||
|
|
f79d24eb01 | ||
|
|
f4c0d608a4 |
没有与搜索条件相匹配的文件
+3
@@ -37,3 +37,6 @@ venv/
|
||||
|
||||
# Local reference binaries stay outside source history
|
||||
reference-binaries/
|
||||
|
||||
# Reviewed signed release installer (intentional distribution artifact)
|
||||
!releases/1.2.0/*.pkg
|
||||
二进制文件未显示。
|
之后 宽度: | 高度: | 大小: 2.6 MiB |
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Dayu
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
基于本仓库的 FxPlug4 初始工程制作的人像美化滤镜,支持 Motion 和 Final Cut Pro。源码使用 Objective-C、Vision、Core Image 与 Metal。
|
||||
|
||||
功能包括磨皮、肤色、细节、牙齿美白、眼睛提亮、九类脸型调整、画面选脸、三目标分析跟踪扩展和蒙版预览。原版 MSE18V2 的静态分析依据、近似实现与尚未对齐的部分见 [分析说明](docs/analysis/REA-分析与复刻说明.md)。本版尚未完成原版逐像素与复杂运动跟踪对照。
|
||||
功能包括磨皮、肤色、细节、牙齿美白、眼睛提亮、九类脸型调整、自动人脸跟随和多人处理。原版 MSE18V2 的静态分析依据、近似实现与尚未对齐的部分见 [分析说明](docs/analysis/REA-分析与复刻说明.md)。本版尚未完成原版逐像素与复杂运动跟踪对照。
|
||||
|
||||
## 使用
|
||||
|
||||
正式安装路径为 `/Library/Plug-Ins/FxPlug/beatygril.app`。Motion 滤镜分类为「大鱼」,滤镜名「大鱼人像美化」。Final Cut Pro 在「效果 → 大鱼 → 大鱼人像美化」中使用配套 Motion 效果,模板目录必须同时保留 `.moef` 与 `Drop Zone.tiff`。美化会随当前帧的人脸自动更新,不需要先点分析;默认脸型跟随主要人脸,开启多人高速模式后对多脸变形。可按需调中频磨皮、肤色与脸型;「只处理选中人脸」用于画面重选目标。三个目标的向前/向后分析属于可选扩展,数据可随工程保存。
|
||||
正式安装路径为 `/Library/Plug-Ins/FxPlug/beatygril.app`。Motion 滤镜分类为「大鱼」,滤镜名「大鱼人像美化」。Final Cut Pro 在「效果 → 大鱼磨皮瘦脸 → 大鱼人像美化」中使用配套 Motion 效果,模板目录必须同时保留 `.moef` 与 `Drop Zone.tiff`。美化会随当前帧的人脸自动更新,不需要点击分析。界面以磨皮、美白、瘦脸三张卡片呈现,默认跟随主要人脸;多人模式对多脸变形。单项眼睛可暂时停用并保留数值,右侧箭头恢复默认;磨皮半径、饱和度、锐化和整体混合等收在「更多设置」。旧手动分析与调试参数仅保留隐藏兼容,实际输出统一使用实时自动跟随。
|
||||
|
||||
## 开发和恢复
|
||||
|
||||
@@ -21,7 +21,7 @@ xcodebuild -project beatygril/beatygril.xcodeproj -scheme 'Wrapper Application'
|
||||
|
||||
工程从 `/Library/Developer/SDKs/FxPlug.sdk` 读取头文件与链接信息,从 `/Library/Developer/Frameworks` 嵌入可运行的 Apple 框架。构建默认使用团队 B3W25FX5H5 的 Developer ID Application 身份;正式交付使用 `python3 scripts/deliver.py` 完成公证、粘附和安装;安装前先关闭 Motion 与 FCP,脚本在检测到宿主仍运行时保留原安装版并停止。原生验收完成后继续脚本的清理阶段,清除临时注册与 APP 副本。
|
||||
|
||||
私有源码仓库:`https://dayu.media/git/dayu/paintfcp-fxplug.git`。恢复入口见 [RECOVERY.md](RECOVERY.md),当前验证记录见 [验证记录](docs/验证记录.md)。原始模板保存在 `baseline-20261021` 标签。
|
||||
源码仓库:`https://dayu.media/git/dayu/paintfcp-fxplug.git`。恢复入口见 [RECOVERY.md](RECOVERY.md),当前验证记录见 [验证记录](docs/验证记录.md)。原始模板保存在 `baseline-20261021` 标签。
|
||||
|
||||
## 1.0.5 瘦脸修复
|
||||
|
||||
@@ -36,3 +36,25 @@ xcodebuild -project beatygril/beatygril.xcodeproj -scheme 'Wrapper Application'
|
||||
旧版分析记录无法区分是否包含额外翻转,新版保留原字符串但暂用实时跟随;重新分析即可写入新坐标标记并恢复离线跟踪。最终Motion/FCP画面由用户自行测试。
|
||||
|
||||
1.0.6(8)已完成正式签名、Apple公证、票据、安装和模板同步。904项完整回归及独立远程恢复核验通过;最终宿主视觉由用户自测。交付回执在`docs/verification/v1.0.6/`,恢复标签为`v1.0.6`。
|
||||
|
||||
## 1.1.0 原生卡片面板
|
||||
|
||||
使用AppKit自绘卡片、滑块和开关,原生数值输入、肤色圆点、单项/分组启停及重置。FCP仅发布自定义面板210和两项版权信息,撤掉平铺参数与手动分析板块。新面板使用独立NSSecureCoding状态对象完整保存设置,支持复制、撤销数据和数值插值;未编辑时保持旧文档参数值。旧分析数据不参与实时渲染。
|
||||
|
||||
`./scripts/test-inspector.sh`验证参数路由、保存恢复、事务、绕过/重置和离屏布局;`python3 scripts/inspector-template.py audit Templates/大鱼人像美化/大鱼人像美化.moef`验证模板自定义数据和发布入口。离屏截图和模拟宿主检查不能替代Motion/FCP的实际嵌入、键盘/VoiceOver和工程重开验收,仍由用户自测。完整1039项回归通过,Inspector/宿主适配/OSC启用ASan。离屏演示图见[面板预览](docs/verification/v1.1.0/inspector-preview.png)。1.1.0(9)已完成通用Release正式签名、Apple公证、票据粘附、唯一正式目录安装及配套模板同步。签名源码`f79d24eb0185f31feaae58b83b632f92dcb3ca91`的111文件及16项必要资源已独立远端恢复核验,clone内134项Inspector ASan和模板审计通过。
|
||||
|
||||
正式安装回执见`docs/verification/v1.1.0/installed-verification.json`,恢复标签为`v1.1.0`。签名、票据、Gatekeeper、双架构身份、PluginKit正式路径及配套模板字节核验通过;已清理本次临时APP、旧APP备份和公证ZIP。有界扫描只发现正式路径的产品外层APP,未把该范围表述为所有卷的完整扫描。未操作Motion/FCP界面,实际面板嵌入与工程重开由用户自测。
|
||||
|
||||
## 1.1.2 面板交互与下半脸定位修复
|
||||
|
||||
面板改为先在真实视图的宿主 action 内取得读写接口,修复控件一直禁用的问题。短脸改用76点下半脸闭合区域和鼻梁位移轴,下颌骨改为六组相邻轮廓半径的局部位移,移除旧脸框近似场;详见[分析](docs/analysis/短脸与下颌骨分析.md)。其余五项脸型仍保留近似实现,不能将局部算法恢复视为全部效果逐像素一致。
|
||||
|
||||
1551项回归通过,含335项宿主适配、98项OSC和157项面板ASan检查;28种控制/原点组合均核验实际解剖位置,改变原点标签时逐字节一致。额外真实帧ASan专项886项通过,含重复基础用例。1.1.2(13)已完成正式签名、公证、票据、唯一正式路径安装及副本清理。Motion/FCP原生数值、重置和隔离画面响应已验证,Motion保存重开与撤销通过;用户于2026-10-22实测确认“拖动中已持续更新”;未指定测试宿主,不扩张为两个宿主分别完成动态排帧验收。连续步进在松手前提交已通过独立模拟宿主验证,不能将它等同于每个鼠标事件都排出一帧。
|
||||
|
||||
## 1.2.0 免费开源发布
|
||||
|
||||
采用 MIT 许可(第三方资源边界见 THIRD_PARTY_NOTICES.md)。安装包同时包含唯一正式 FxPlug4 APP 和 FCP 效果模板;自动注册,无需账号、激活码或付费激活。主 APP 显示古风说明图,窗口显示三秒后自动退出,不影响独立的宿主插件服务。封面人物是生成的宣传插画,非效果前后对比。
|
||||
|
||||
`large.png`(640×360)和 `small.png`(192×108)与 `.moef` 同目录,安装器安装后重新锁定。手动复制或从 Git 恢复模板时,需对这两个 PNG 重新执行 `chflags uchg`,不能锁定整个模板目录。FCP 当前分类为「大鱼磨皮瘦脸」,Motion 原生滤镜分类仍为「大鱼」。
|
||||
|
||||
仓库属性已设为公开;大鱼站统一 Git 网关目前仍要求登录并校验会员,公开属性并未绕过网关。发布文章另提供可公开下载的当前源码快照,不依赖 Git 登录;安装包始终免费。
|
||||
+40
-2
@@ -1,6 +1,6 @@
|
||||
# 恢复说明
|
||||
|
||||
- 私有远程:`https://dayu.media/git/dayu/paintfcp-fxplug.git`
|
||||
- 源码远程:`https://dayu.media/git/dayu/paintfcp-fxplug.git`
|
||||
- 本地项目根:`/Users/dayu/Documents/PaintFCP`
|
||||
- 工作分支:`main`
|
||||
- 修改前版本标签:`baseline-20261021`,完整 SHA:`1746f38398588e2367abb37a5c2b31cb64042429`
|
||||
@@ -12,7 +12,7 @@
|
||||
```sh
|
||||
git clone https://dayu.media/git/dayu/paintfcp-fxplug.git
|
||||
cd paintfcp-fxplug
|
||||
git switch --detach v1.0.6
|
||||
git switch --detach v1.2.0
|
||||
```
|
||||
|
||||
需要继续编辑时,基于该提交创建新分支。安装 Xcode 与 Apple FxPlug 4 SDK;盘点版本和构建入口见 README.md。SDK/framework 为 Apple 外部开发依赖,应重新安装匹配版本。证书、私钥和公证凭据需从独立加密备份恢复或重新申请;Git 不包含它们。
|
||||
@@ -58,3 +58,41 @@ FCP模板从 `Templates/大鱼人像美化/` 恢复,将 `.moef` 和 `Drop Zone
|
||||
`ad54726b17fabd11f46bc6121e664eac38960a23`已独立从上述远程克隆,87文件的byte/blob/mode与14项资源一致,strict/full fsck和559项核心测试通过,无LFS、子模块或APP产物。临时恢复目录已清理,完整报告见`docs/verification/v1.0.6/recovery-ad54726.json`。
|
||||
|
||||
1.0.6已完成正式签名、公证(Accepted ab0271c8-0c4b-4b10-9474-cebe9b55385b)、票据粘附、唯一正式目录安装和配套模板同步;临时APP/旧备份APP/公证ZIP已清理。完整回执及有界副本扫描见`docs/verification/v1.0.6/installed-verification.json`。未操作Motion/FCP界面,最终视觉验收保留为用户自测。
|
||||
|
||||
## 1.1.0 面板与自动处理
|
||||
|
||||
1.1.0(9)使用自有原生卡片面板和PGInspectorState安全归档。源代码、面板资源(全部程序绘制)、Motion模板与自有自定义参数数据均在仓库;新增`./scripts/test-inspector.sh`及`scripts/inspector-template.py audit`验证入口。恢复时需完整保留`.moef`中的自定义参数210、实际dataValue/defaultVal和两项版权发布;不要删掉仍用于旧工程迁移的隐藏参数定义和组路径。不得拿其他插件的自定义归档替换本产品自己的归档。
|
||||
|
||||
新渲染统一实时自动人脸跟随,旧手动分析数据仍保存在旧文档中但不参与本版渲染;主界面不再显示前后分析、目标列表、蒙版调试与平铺控件。三张卡片之外的细调收在更多设置。自定义对象保存完整界面状态,首次编辑前兼容旧参数;数值、开关、启停mask按已测试的复制/插值规则恢复。真实宿主UI及工程重开仍需用户自测。
|
||||
|
||||
正式构建、公证、安装仍使用同一`python3 scripts/deliver.py --wait-for-hosts --user-tests`,保持固定Bundle ID与APP路径,仅在用户自行退出Motion/FCP后替换,最终版本节点与回执随交付记录补齐。
|
||||
|
||||
兼容边界:未编辑面板时读取旧文档原参数;首次编辑会把当前数值采集为完整面板状态。旧版各项独立关键帧曲线尚未自动迁移为新面板关键帧,不宣称编辑前后任意旧动画逐帧等价。面板本身的数值插值与开关保持已做离屏验证,实际宿主关键帧交互仍待用户测试。
|
||||
|
||||
1.1.0签名构建源码为完整SHA`f79d24eb0185f31feaae58b83b632f92dcb3ca91`,源码工作区在构建时干净。该节点已独立从大鱼远程克隆,111文件的bytes/blob/mode及16项必要资源一致,strict/full fsck、134项Inspector ASan和模板审计通过,无LFS或子模块,临时克隆已清理。报告见`docs/verification/v1.1.0/recovery-f79d24e.json`。Apple公证Accepted(ce26be63-9dcd-436a-bcb5-bd294fcc92a9)和票据验证已通过;安装前曾等待用户自行退出宿主,随后完成下述正式替换。
|
||||
|
||||
1.1.0(9)现已安装到固定正式路径;APP与嵌套插件为arm64/x86_64通用版本,Developer ID团队、可信时间戳、Hardened Runtime、严格签名、公证票据、Gatekeeper及PluginKit正式路径全部核验通过。配套`.moef` SHA256为`e1c0cff1dd3541ed97cab2cb39a76ad4f28dc659b3be7991cc7ac92dd8ea090b`,和TIFF一起同步到正式模板目录并逐字节核对。本次临时/旧APP及公证ZIP已清理;扫描范围和回执在`docs/verification/v1.1.0/`。最终恢复标签为`v1.1.0`,后续记录提交不改变签名产品源码f79d24e。原生UI和视觉仍由用户自测。
|
||||
|
||||
交付偏好(2026-10-21用户明确更新):后续本产品安装需释放Motion时,可直接结束Motion进程,不再重复请求保存退出;用户说明不在Motion里进行工作。保持不自动操作Motion/FCP界面的约定。本次检查时两个宿主已经退出,没有执行强退。
|
||||
|
||||
## 1.1.2 合并修复准备
|
||||
|
||||
版本1.1.2(13)合并面板action上下文修复和76点短脸/下颌骨局部场。构建/恢复依赖、正式路径、签名公证入口保持不变;自动检查1551项及额外886项真实帧ASan专项通过。原生验收使用build/native-112独立Motion副本,用户素材、截图和完整AX数据不随源码上传。用户已明确授权接手Motion/FCP进行测试;先前不操作界面的偏好已被本次授权更新。
|
||||
|
||||
## 1.1.2 正式恢复入口
|
||||
|
||||
发布标签`v1.1.2`,版本1.1.2(13),正式签名源码完整SHA为`a2e33e8e00a99b95dc29d67a819cb0897eda5756`;后续交付记录不改变产品源码。独立远程恢复核验149个文件的bytes/blob/mode、16项资源、strict/full fsck和157项Inspector ASan全部通过,无LFS或子模块。恢复依赖与构建入口同前文;`PAINTFCP_INSPECTOR_ASAN=1 ./scripts/test-inspector.sh`可运行新增数值焦点与每步预览检查。
|
||||
|
||||
正式Release已使用固定团队逐层签名,Apple公证Accepted `5f5ce378-8430-467b-ae97-048e6bc81573`,票据、Gatekeeper、通用架构和PluginKit正式路径全部通过。唯一正式安装APP为`/Library/Plug-Ins/FxPlug/beatygril.app`,模板moef SHA256为`e9fa97dc14323d0daa7d3d61b06a78ba4000a70ab9f5e67ebffd750d49df1e15`,TIFF逐字节一致。临时APP、旧APP和公证ZIP已清理,有界目录及现存Spotlight索引产品路径中仅发现正式APP,扫描错误0,不宣称扫描了所有卷。
|
||||
|
||||
Motion/FCP原生验收范围及未完成项见`docs/verification/v1.1.2/native-qa.json`:数值、重置、隔离短脸/下颌骨响应通过;Motion撤销和保存关闭重开通过。连续步进已改为每步完整宿主action,模拟宿主确认鼠标松开前可发布中间设置;用户于2026-10-22实测确认拖动中持续更新;未指定宿主,未测量逐事件排帧时序。后续人脸素材和全AX只保留本机build,不是产品依赖或上传资源。
|
||||
|
||||
最终标签`v1.1.2`完整提交SHA为`e9fddda5e13042f761755762fa39dfe090853c6a`,已独立从远端恢复,158文件的bytes/blob/mode与strict/full fsck通过,产品目录与签名源码a2e33e8逐字节一致,无LFS或子模块,恢复临时目录已清理。详见`docs/verification/v1.1.2/final-tag-recovery.json`;后续main仅补充恢复报告和用户验收记录,产品源码保持不变。
|
||||
|
||||
## 1.2.0 恢复入口
|
||||
|
||||
源码构建节点为 `696d5f39b30404ed5d1d4c25f61d17475c7fc91b`,版本1.2.0(14)。使用同一 Apple SDK、签名团队与正式安装路径;本次仅改变外层启动器、版本及宣传资源,渲染和面板保留1.1.2验收实现。最终安装包和验证记录随 `v1.2.0` 保存。无 LFS / 子模块。正式 APP 只保留 `/Library/Plug-Ins/FxPlug/beatygril.app`;源代码不包含私钥。
|
||||
|
||||
模板恢复到 `~/Movies/Motion Templates.localized/Effects.localized/大鱼磨皮瘦脸/大鱼人像美化/`,需同时保留 `.moef`、`Drop Zone.tiff`、`large.png`、`small.png`。Git 不保存 BSD 文件标志,手工复制后对两张 PNG 执行 `chflags uchg` 并核对 `ls -lO`;不要锁目录和工程。自动安装 PKG 会恢复这两个锁。
|
||||
|
||||
仓库已为公开属性,但当前网站 Git 网关仍执行登录 / 会员校验。匿名 `ls-remote` 未通过,不宣称无需登录的 Git 恢复已通过;认证的远端克隆和文章的公开源码快照分别核验。服务端备份边界与前文相同。
|
||||
@@ -0,0 +1,12 @@
|
||||
# 第三方资料与许可边界
|
||||
|
||||
本项目自己的 Objective-C、Metal、脚本及原创文档按根目录 MIT License 开源。署名 Dayu 不表示第三方代码、样本与 SDK 的版权转移。
|
||||
|
||||
- Apple FxPlug / PluginManager SDK 和随正式插件分发的运行框架属于 Apple;受 Apple 相应 SDK 与再分发条款约束,源码仓库不包含这些框架。编译需自行安装 Xcode 与 FxPlug SDK。macOS Vision、AppKit、CoreImage 属于系统组件。
|
||||
- `Templates/大鱼人像美化/Drop Zone.tiff` 是 Motion 工程的标准占位资源,随模板保留;不将其声明为 Dayu 原创作品或另行授予 MIT 权利。
|
||||
- `tests/fixtures/astronaut.png` 来源于 NASA 的 Eileen Collins 图像,经 scikit-image 样本分发,详见同目录 ATTRIBUTION.md。相关分析预览由该公开样本生成。
|
||||
- `docs/analysis` 中的对照分析、符号和反汇编摘录是互操作研究材料,不包括目标商业 APP、模型或 metallib。MIT 不为其原权利人的内容重新授权。本项目不声称与目标商业插件的模型或画面逐像素一致。
|
||||
- `Assets/Artwork/portrait-cover.png`、APP 中 `LauncherInfo.png` 及两张模板缩略图由内置图像生成工具创作。人物为虚构成年古风人物,封面是宣传插画,不是真实滤镜前后效果对比;生成说明和提示词见 `docs/design`。未使用目标插件的宣传人像。
|
||||
- 不包含用户测试素材、用户工程、真实人脸坐标数据、签名私钥、公证密码或账号令牌。签名证书与公证凭据不属于源码恢复范围。
|
||||
|
||||
免费发布版本无需激活码、付费注册或联网授权。安装时执行系统插件注册;主 APP 打开后显示说明并在三秒后退出,宿主内的 FxPlug 服务独立运行。
|
||||
+206
-51
@@ -1,9 +1,28 @@
|
||||
{
|
||||
"version": "1.0.2",
|
||||
"version": "1.1.0",
|
||||
"pluginUUID": "FB14B0E6-59B2-4836-916F-CD0C559E9241",
|
||||
"filterObject": "510000005",
|
||||
"visiblePublished": 46,
|
||||
"visiblePublished": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"id": 210,
|
||||
"name": "",
|
||||
"channel": "./210",
|
||||
"kind": "custom-archive",
|
||||
"class": "PGInspectorState",
|
||||
"published": true,
|
||||
"hasEdits": false,
|
||||
"schema": 1,
|
||||
"templateFlags": "12901679104",
|
||||
"panelCards": [
|
||||
"磨皮",
|
||||
"美白",
|
||||
"瘦脸"
|
||||
],
|
||||
"secondarySection": "更多",
|
||||
"valueStorage": "Own secure keyed archive; Custom Data inside BlindDataObject, DataIsLegacy=false",
|
||||
"nativeLoadingValidation": "pending user"
|
||||
},
|
||||
{
|
||||
"id": 70,
|
||||
"name": "启用磨皮",
|
||||
@@ -12,7 +31,9 @@
|
||||
"kind": "bool",
|
||||
"default": 1,
|
||||
"value": 1,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
@@ -22,7 +43,9 @@
|
||||
"kind": "float",
|
||||
"default": 1,
|
||||
"value": 1,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
@@ -32,7 +55,9 @@
|
||||
"kind": "float",
|
||||
"default": 0,
|
||||
"value": 0.5,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
@@ -42,7 +67,9 @@
|
||||
"kind": "float",
|
||||
"default": 8,
|
||||
"value": 8,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 30,
|
||||
@@ -52,7 +79,9 @@
|
||||
"kind": "bool",
|
||||
"default": 1,
|
||||
"value": 1,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 71,
|
||||
@@ -62,7 +91,9 @@
|
||||
"kind": "bool",
|
||||
"default": 1,
|
||||
"value": 1,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 42,
|
||||
@@ -72,7 +103,9 @@
|
||||
"kind": "popup",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12901744656"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
@@ -82,7 +115,9 @@
|
||||
"kind": "float",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
@@ -92,7 +127,9 @@
|
||||
"kind": "float",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
@@ -102,7 +139,9 @@
|
||||
"kind": "float",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 41,
|
||||
@@ -112,7 +151,9 @@
|
||||
"kind": "float",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 31,
|
||||
@@ -122,7 +163,9 @@
|
||||
"kind": "float",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 32,
|
||||
@@ -132,7 +175,9 @@
|
||||
"kind": "float",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 73,
|
||||
@@ -142,7 +187,9 @@
|
||||
"kind": "bool",
|
||||
"default": 1,
|
||||
"value": 1,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
@@ -152,7 +199,9 @@
|
||||
"kind": "float",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
@@ -162,7 +211,9 @@
|
||||
"kind": "float",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 72,
|
||||
@@ -172,7 +223,9 @@
|
||||
"kind": "bool",
|
||||
"default": 1,
|
||||
"value": 1,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
@@ -182,7 +235,9 @@
|
||||
"kind": "float",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 33,
|
||||
@@ -192,7 +247,9 @@
|
||||
"kind": "float",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 22,
|
||||
@@ -202,7 +259,9 @@
|
||||
"kind": "float",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 23,
|
||||
@@ -212,7 +271,9 @@
|
||||
"kind": "float",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 24,
|
||||
@@ -222,7 +283,9 @@
|
||||
"kind": "float",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 25,
|
||||
@@ -232,7 +295,9 @@
|
||||
"kind": "float",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 26,
|
||||
@@ -242,7 +307,9 @@
|
||||
"kind": "float",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 27,
|
||||
@@ -252,7 +319,9 @@
|
||||
"kind": "float",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 28,
|
||||
@@ -262,7 +331,9 @@
|
||||
"kind": "float",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 29,
|
||||
@@ -272,7 +343,9 @@
|
||||
"kind": "float",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
@@ -282,7 +355,9 @@
|
||||
"kind": "bool",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 21,
|
||||
@@ -292,7 +367,9 @@
|
||||
"kind": "point",
|
||||
"default": 0.5,
|
||||
"value": 0.5,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905943072"
|
||||
},
|
||||
{
|
||||
"id": 39,
|
||||
@@ -302,7 +379,9 @@
|
||||
"kind": "bool",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 40,
|
||||
@@ -312,7 +391,9 @@
|
||||
"kind": "float",
|
||||
"default": 1.8,
|
||||
"value": 1.8,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 45,
|
||||
@@ -322,7 +403,9 @@
|
||||
"kind": "bool",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 46,
|
||||
@@ -332,7 +415,9 @@
|
||||
"kind": "popup",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12901744656"
|
||||
},
|
||||
{
|
||||
"id": 50,
|
||||
@@ -342,7 +427,9 @@
|
||||
"kind": "button",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": null
|
||||
},
|
||||
{
|
||||
"id": 51,
|
||||
@@ -352,7 +439,9 @@
|
||||
"kind": "button",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": null
|
||||
},
|
||||
{
|
||||
"id": 52,
|
||||
@@ -362,7 +451,9 @@
|
||||
"kind": "button",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": null
|
||||
},
|
||||
{
|
||||
"id": 53,
|
||||
@@ -372,7 +463,9 @@
|
||||
"kind": "status",
|
||||
"default": "选择画面中的人脸后开始分析",
|
||||
"value": "选择画面中的人脸后开始分析",
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
@@ -382,7 +475,9 @@
|
||||
"kind": "bool",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 35,
|
||||
@@ -392,7 +487,9 @@
|
||||
"kind": "bool",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
@@ -402,7 +499,9 @@
|
||||
"kind": "float",
|
||||
"default": 148,
|
||||
"value": 148,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
@@ -412,7 +511,9 @@
|
||||
"kind": "float",
|
||||
"default": 0.6,
|
||||
"value": 0.6,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
@@ -422,7 +523,9 @@
|
||||
"kind": "float",
|
||||
"default": 0.4,
|
||||
"value": 0.4,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
@@ -432,7 +535,9 @@
|
||||
"kind": "bool",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 43,
|
||||
@@ -442,7 +547,9 @@
|
||||
"kind": "bool",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 44,
|
||||
@@ -452,7 +559,9 @@
|
||||
"kind": "popup",
|
||||
"default": 0,
|
||||
"value": 0,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12901744656"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
@@ -462,7 +571,9 @@
|
||||
"kind": "float",
|
||||
"default": 1,
|
||||
"value": 1,
|
||||
"published": true
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 60,
|
||||
@@ -470,7 +581,9 @@
|
||||
"channel": "./60",
|
||||
"kind": "hidden-string",
|
||||
"value": "",
|
||||
"published": false
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 61,
|
||||
@@ -478,7 +591,9 @@
|
||||
"channel": "./61",
|
||||
"kind": "hidden-string",
|
||||
"value": "",
|
||||
"published": false
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
},
|
||||
{
|
||||
"id": 62,
|
||||
@@ -486,7 +601,9 @@
|
||||
"channel": "./62",
|
||||
"kind": "hidden-string",
|
||||
"value": "",
|
||||
"published": false
|
||||
"published": false,
|
||||
"hiddenCompatibility": true,
|
||||
"templateFlags": "12905938976"
|
||||
}
|
||||
],
|
||||
"effectSource": {
|
||||
@@ -498,5 +615,43 @@
|
||||
"dropZone": 1,
|
||||
"path": "Drop Zone.tiff",
|
||||
"resourceBundled": true
|
||||
},
|
||||
"informationalPublished": 2,
|
||||
"totalPublished": 3,
|
||||
"legacyDefinitionCount": 49,
|
||||
"legacyGroupsPreserved": [
|
||||
100,
|
||||
101,
|
||||
102,
|
||||
103,
|
||||
104,
|
||||
105
|
||||
],
|
||||
"nativeHiddenPolicy": "All legacy groups and parameters remain registered with SDK kFxParameterFlag_HIDDEN. Known scalar/point/string hidden XML flags are copied from observed native structures; group/popup/button private flags are preserved.",
|
||||
"publishSettings": [
|
||||
{
|
||||
"object": "510000011",
|
||||
"channel": "./2/100",
|
||||
"name": "FCP 版本作者→"
|
||||
},
|
||||
{
|
||||
"object": "510000012",
|
||||
"channel": "./2/100",
|
||||
"name": "更多模板浏览器访问→"
|
||||
},
|
||||
{
|
||||
"object": "510000005",
|
||||
"channel": "./210",
|
||||
"name": ""
|
||||
}
|
||||
],
|
||||
"defaults": {
|
||||
"legacyMidtoneDefault": 0,
|
||||
"legacyMidtoneValue": 0.5,
|
||||
"customStateHasEdits": false,
|
||||
"initialEffectiveMidtone": 0.5,
|
||||
"publishOSC": 0,
|
||||
"manualAnalysisVisible": false,
|
||||
"trackingMode": "automatic primary realtime follow"
|
||||
}
|
||||
}
|
||||
+26
-20
@@ -1,37 +1,43 @@
|
||||
# 大鱼人像美化 FCP 效果模板
|
||||
|
||||
版本:1.0.6。滤镜 UUID:`FB14B0E6-59B2-4836-916F-CD0C559E9241`。模板必须与已安装的大鱼人像美化 FxPlug APP 配套使用。
|
||||
版本:1.1.2。滤镜 UUID:`FB14B0E6-59B2-4836-916F-CD0C559E9241`。需配套同版大鱼人像美化 FxPlug APP。
|
||||
|
||||
## 默认数值
|
||||
## 三卡片与自动跟随
|
||||
|
||||
模板首次应用时,“中频磨皮”为 **50%(0.5)**;直接在 Motion 添加裸 FxPlug 滤镜时为 **0%(0)**。模板保存了 `default=0 / value=0.5`,保留裸插件的重置基准;其他功能参数与插件默认一致。整体混合为100%,脸型、肤色提亮、锐化、牙齿和眼睛提亮初始为0,跟踪初始关闭。此模板不预置人脸身份或跟踪记录。
|
||||
FCP 发布列表仅保留作者、网站两个空绑定展示项,以及一个空名称的自定义面板入口 `./210`,共3项。面板内提供“磨皮、美白、瘦脸”三张卡片和“更多”附加设置。主体人脸由当前画面自动选择并实时跟随,无需手动前向/后向分析。多人模式、预览框、防跳脸与各项调节由插件自定义面板承载。
|
||||
|
||||
## 参数与输入
|
||||
原有46个平铺功能入口已撤下。旧49个参数定义及六组路径仍保留供旧项目兼容,插件以 SDK 隐藏标记注册;旧手动分析按钮、跟踪目标/状态、选脸点和目标数据不再发布为界面入口。隐藏的目标数据60/61/62不删除。发布 OSC 初始为0;旧OSC类注册由插件保留。
|
||||
|
||||
- 保留磨皮、肤色、细节、脸型、人脸选择与跟踪、蒙版与输出六个原生 FxPlug 参数组,发布46项可见功能参数,包括前向/后向分析及清除按钮。
|
||||
- 目标1–3的隐藏分析数据以参数60/61/62保留在滤镜节点中,不发布成用户控件;分析完成后由插件写入宿主参数并随工程保存。
|
||||
- 选脸位置发布为原生二维参数,并启用滤镜的“发布 OSC”宿主通道。跟踪按钮、OSC与工程关闭重开后的数据恢复须由实际 FCP 验证。
|
||||
- 画面只含一个标准 Effect Source(Type=3、Drop Zone=1),其 Source Media 指向 clip `510000006`,clip 位于 footage `510000007`。`pathURL=Drop Zone.tiff` 为模板同目录内的相对引用,标准1200×1200占位图已随模板附带,没有外部安装目录依赖、示例人像、旧滤镜或新动画。FCP时间线片段的实际替换已在独立验收项目中确认正常出图。
|
||||
- 作者与网站为两个原生空绑定展示菜单,不改变渲染。
|
||||
## 默认与旧项目
|
||||
|
||||
建议模板安装位置:`~/Movies/Motion Templates.localized/Effects.localized/大鱼/大鱼人像美化/`。APP正式安装与模板安装、Motion/FCP原生验收由主任务处理。
|
||||
模板首次应用时,旧原生“中频磨皮”仍保存 `default=0 / value=0.5`,即初始局部磨皮50%;直接添加裸插件时为0%。自定义 `PGInspectorState` 初始 `hasEdits=NO`,因此读取并保留旧原生参数,不会用面板归档中的默认0覆盖模板0.5或旧项目数值。面板经过编辑后由自定义状态持久保存调节值。此模板不预置任何人脸身份或跟踪记录。
|
||||
|
||||
## 结构来源与静态验证
|
||||
## 自有状态归档
|
||||
|
||||
Apple项目、群组、Effect Source和滤镜节点骨架来源于用户自有“大鱼去黑.moef”;仅保留这些原生结构,已替换滤镜UUID、名称、版本和全部功能参数。FxPlug组/字符串/点参数的原生结构交叉参照自有Number与Zoom and Pan样本;作者/网站空绑定来自ae-to-motion技能的原生版权样本。
|
||||
参数210使用本插件生成的 `PGInspectorState` 安全键控归档:内部 `Custom Data` 对象、外部 `BlindDataObject` 与 `DataIsLegacy=false`,再按已核对的Motion文本编码和长度封装。保存 `schema=1 / edited=false`,不使用原版私有数据、类名、品牌资源或空 `<dataValue>` 猜测初始化。默认和当前归档相同,可完整解码并往返核验。
|
||||
|
||||
`PARAMETERS.json`逐项记录参数ID、组、发布通道及初始值;`VALIDATION.json`记录本次静态核验。48个发布路径(46功能+2信息)均能反查实际对象和通道,实体ID无冲突,三个隐藏数据通道未发布;必要占位媒体以相对路径随包附带,保存选择状态为0个内容对象且焦点为项目。技能`motion_selection.py check`与`audit_motion.py --require-no-selection`通过。
|
||||
该编码依据原生文件静态结构及独立Foundation归档检查;1.1.0自定义面板在Motion/FCP中的实际创建、显示、交互、撤销和关闭重开恢复仍待用户验收,不能以XML可解析或归档解码成功代替宿主加载通过。
|
||||
|
||||
结构核验之外,已用实际素材在FCP和Motion完成加载、画面及提亮回归测试。详细范围及尚未覆盖的边界见 [验证记录](../../docs/验证记录.md)。
|
||||
## 输入、媒体与版权
|
||||
|
||||
## 前版输入链错误与本次修复
|
||||
输入链沿用已修复的标准 Effect Source(Type=3、Drop Zone=1):Source Media→clip `510000006`→footage `510000007`→相对文件 `Drop Zone.tiff`。该占位图随模板附带,420128字节,SHA256 `a7f8e3a2a431dbb3ce9e777040bb55e701cfe9ab9632e8cbb10359732bc8692e`。没有外部素材、示例人像或新动画。
|
||||
|
||||
前版生成稿(SHA256 `3b7984e11a92487834ab67121fd3814bf13c22b77a70851298efa84206f91478`)将效果源的 Source Media 设为0,并删除了原生clip/footage。用户在FCP应用后实测整幅黑屏。这是模板装配错误;保留Type=3本身并不能证明FCP输入替换链完整。此前XML解析、发布路径和无缺媒体检查通过,也没有覆盖真实FCP画面输入,不能当作该模板已经可用的证据。
|
||||
作者与网站仍是同一个Rig下的两个无受控参数菜单:“FCP 版本作者→/大鱼先森Media”和“更多模板浏览器访问→/dayu.media”。仅展示信息,不改变渲染。
|
||||
|
||||
黑屏修复恢复标准效果源clip/footage和Source Media关联,并随包加入标准`Drop Zone.tiff`,保留49个插件参数、48个发布入口与Type=3/Drop Zone=1。1.0.4同时包含独立的提亮蒙版与选脸坐标修复。该占位图从Apple Motion的Ozone.framework标准资源复制,实际运行只引用本模板内的相对副本。图像SHA256为 `a7f8e3a2a431dbb3ce9e777040bb55e701cfe9ab9632e8cbb10359732bc8692e`,420128字节。
|
||||
正式安装位置:`~/Movies/Motion Templates.localized/Effects.localized/大鱼/大鱼人像美化/`。1.1.0模板和占位TIFF已同步并逐字节核验,配套APP已正式签名、公证、安装;没有操作Motion/FCP界面,用户自测原生面板。
|
||||
|
||||
本次已经静态核验Source Media→clip→相对文件链存在、文件字节一致、48项发布仍解析、隐藏数据未发布和保存选择状态;修复后已在FCP独立验收项目新增实例,确认输入替换、实际人像和提亮画面正常;Mix=0逐字节直通由宿主适配器IOSurface回归测试覆盖,未将其混称为FCP截图逐像素核验。
|
||||
## 验证边界与历史修复
|
||||
|
||||
1.0.5将瘦脸改为76点驱动的17组局部位移,供用户在Motion/FCP自测最大瘦脸与转脸。旧跟踪记录若没有全点阵,需重新分析后才有瘦脸,或关闭“使用跟踪数据”采用当前帧自动检测。
|
||||
`PARAMETERS.json`记录新旧参数和3条发布路径;`VALIDATION.json`分开记录结构/归档核验与当前宿主待验项。内容选择状态保持为空,效果源、clip/footage、占位资源和版权绑定结构均保留。
|
||||
|
||||
1.0.6移除Core Image/IOSurface链按宿主原点重复翻转,修正下巴三组局部位移及正负方向。旧版未标记坐标空间的分析记录保留但不再重放,自动回退实时跟随;需要离线记录时重新分析当前目标。
|
||||
早期模板曾误删clip/footage、将Source Media设为0,用户在FCP实测黑屏;随后恢复完整标准输入链,历史版本已实际出图。该历史结果只支持输入链,不能证明新1.1.0自定义面板已加载。1.0.5加入76点17组局部瘦脸;1.0.6移除IOSurface重复翻转并修复下巴三组局部位移。旧未标记坐标空间的跟踪数据保留但不重放,自动回退实时跟随。
|
||||
|
||||
## 可复现的只读核验
|
||||
|
||||
在仓库根目录执行:
|
||||
|
||||
```sh
|
||||
python3 scripts/inspector-template.py audit "Templates/大鱼人像美化/大鱼人像美化.moef"
|
||||
```
|
||||
|
||||
脚本可提取内外归档并核验自有类、schema、初始状态、3条发布和媒体引用;`encode --inner <本插件生成的Custom Data归档> --text-output <输出文件>` 可重新包装文本值,不启动宿主。其结果明确保留宿主加载待验状态。
|
||||
+182
-12
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"version": "1.0.6",
|
||||
"sha256": "30ca8edf36801264c5de2c753c40b720aba8052633620713a12401098fcd2409",
|
||||
"version": "1.1.2",
|
||||
"sha256": "e9fa97dc14323d0daa7d3d61b06a78ba4000a70ab9f5e67ebffd750d49df1e15",
|
||||
"filterUUID": "FB14B0E6-59B2-4836-916F-CD0C559E9241",
|
||||
"publishedFunctionalControls": 46,
|
||||
"publishedFunctionalControls": 1,
|
||||
"informationalControls": 2,
|
||||
"hiddenTrackingSlots": 3,
|
||||
"publishedPathsResolved": 48,
|
||||
"publishedPathsResolved": 3,
|
||||
"sourceType": 3,
|
||||
"sourceMedia": "510000006",
|
||||
"clip": "510000006",
|
||||
@@ -23,14 +23,6 @@
|
||||
"problem": "Previous generated template removed the standard source clip and could not establish the native effect-source input chain. XML/reference checks did not validate FCP timeline replacement.",
|
||||
"change": "Restored the user-owned reference template clip/footage skeleton with new unique IDs; restored the Source Media link; bundled standard Drop Zone.tiff; filter, publications and Type=3/Drop Zone=1 preserved."
|
||||
},
|
||||
"hostValidation": {
|
||||
"date": "2026-10-21",
|
||||
"FCP": "Restored input-chain template shows supplied real video; effect parameters and forward analysis responded in independent QA project.",
|
||||
"Motion": "1.0.4 installed formal app loaded the real-video QA project and rendered brightness/geometry with a primary box at different playhead positions; offline tracking disabled.",
|
||||
"scope": "1.0.6 coordinate/chin repair native visual QA is reserved for user self-testing; no Motion/FCP UI operations. Historical host tests remain scoped to their versions.",
|
||||
"version105": "Formal 1.0.5 (7) signed/notarized/stapled/installed and PluginKit formal path verified. User closed hosts before replacement; no Motion/FCP UI operated. Native visual testing pending user.",
|
||||
"version106": "Formal 1.0.6 (8) universal Release signed/notarized/stapled/installed, identity/Gatekeeper/formal PluginKit path verified. User closed hosts; companion moef and TIFF synchronized and byte-verified. Generated outer APPs and notarization ZIP removed. No Motion/FCP UI operations; native visual QA pending user."
|
||||
},
|
||||
"slimValidation": {
|
||||
"offlineFullChecks": 604,
|
||||
"slimChecks": 224,
|
||||
@@ -51,5 +43,183 @@
|
||||
"maxByteErrorVsIndependentCIReference": 2,
|
||||
"maxUVOracleError": 1.09828022e-05,
|
||||
"nativeVisualQA": "pending user"
|
||||
},
|
||||
"historicalHostValidation": {
|
||||
"date": "2026-10-21",
|
||||
"FCP": "Restored input-chain template shows supplied real video; effect parameters and forward analysis responded in independent QA project.",
|
||||
"Motion": "1.0.4 installed formal app loaded the real-video QA project and rendered brightness/geometry with a primary box at different playhead positions; offline tracking disabled.",
|
||||
"scope": "1.0.6 coordinate/chin repair native visual QA is reserved for user self-testing; no Motion/FCP UI operations. Historical host tests remain scoped to their versions.",
|
||||
"version105": "Formal 1.0.5 (7) signed/notarized/stapled/installed and PluginKit formal path verified. User closed hosts before replacement; no Motion/FCP UI operated. Native visual testing pending user.",
|
||||
"version106": "Formal 1.0.6 (8) universal Release signed/notarized/stapled/installed, identity/Gatekeeper/formal PluginKit path verified. User closed hosts; companion moef and TIFF synchronized and byte-verified. Generated outer APPs and notarization ZIP removed. No Motion/FCP UI operations; native visual QA pending user."
|
||||
},
|
||||
"totalPublished": 3,
|
||||
"hiddenCompatibilityParameters": 49,
|
||||
"legacyGroupPathsPreserved": true,
|
||||
"legacyValuesPreserved": true,
|
||||
"publishOSC": 0,
|
||||
"currentHostValidation": {
|
||||
"version": "1.1.2",
|
||||
"build": "13",
|
||||
"Motion": "pass: native number/reset/undo and save-close-reopen; isolated shape/profile images inspected with identical geometry source",
|
||||
"FCP": "pass: formal load, number/reset and same-frame isolated short/jaw render",
|
||||
"continuousPreview": "pass: user confirmed held-drag continuous preview on 2026-10-22 (Asia/Shanghai), version1.1.2 build13; host not specified, per-event frame timing not instrumented"
|
||||
},
|
||||
"customParameter": {
|
||||
"id": 210,
|
||||
"channel": "./210",
|
||||
"name": "",
|
||||
"flags": "12901679104",
|
||||
"flagsOrigin": "Observed original custom parameter native structure, without copying its data blob",
|
||||
"class": "PGInspectorState",
|
||||
"schema": 1,
|
||||
"hasEdits": false,
|
||||
"valueCount": 30,
|
||||
"disabledCount": 0,
|
||||
"classes": [
|
||||
"NSDictionary",
|
||||
"NSSet",
|
||||
"PGInspectorState"
|
||||
],
|
||||
"innerBytes": 935,
|
||||
"innerSHA256": "257540ac6079756a76f3d31dff1b9bca2b33393962057927791316965495ab60",
|
||||
"outerBytes": 1127,
|
||||
"outerSHA256": "95d5f1e2c35c40e9f1234b1480894bcc0ee68c0822c9bd3f7c04338062e6a26b",
|
||||
"encodedCharacters": 1516,
|
||||
"encodingAlphabet": "*-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
|
||||
"envelope": "8-byte big-endian outer archive length, byte 0x2a, outer NSKeyedArchiver payload, zero padding to 3-byte boundary, custom base64 alphabet",
|
||||
"outerTopKeys": [
|
||||
"BlindDataObject",
|
||||
"DataIsLegacy"
|
||||
],
|
||||
"innerTopKey": "Custom Data",
|
||||
"legacyFlag": false,
|
||||
"defaultEqualsCurrent": true,
|
||||
"exactEncodeDecodeRoundtrip": true,
|
||||
"outerSecureDecodeRoundtrip": true,
|
||||
"vendorClassesOrBlobReused": false,
|
||||
"emptyDataValueFallback": "not established and not relied upon",
|
||||
"nativeLoadStatus": "instantiated in Motion and FCP"
|
||||
},
|
||||
"publicationTransition": {
|
||||
"previousFunctionalControls": 46,
|
||||
"currentFunctionalControls": 1,
|
||||
"informationControls": 2,
|
||||
"manualAnalysisPublished": false,
|
||||
"retainedForCompatibility": [
|
||||
45,
|
||||
46,
|
||||
50,
|
||||
51,
|
||||
52,
|
||||
53,
|
||||
60,
|
||||
61,
|
||||
62
|
||||
],
|
||||
"oldParameterPathsUnchanged": true
|
||||
},
|
||||
"xmlFlagPolicy": {
|
||||
"scalarBooleanString": "12905938976; complete observed hidden native flags",
|
||||
"point": "12905943072; complete observed hidden native point flags",
|
||||
"groupPopupButton": "existing private XML flags retained; no guessed bit patch",
|
||||
"pluginHiddenRegistration": "verified in current source: kFxParameterFlag_HIDDEN for all legacy groups, numeric/point/popup/string parameters; commands additionally disabled"
|
||||
},
|
||||
"nonVisualAcceptance": {
|
||||
"XMLAndReferences": "pass: own codec audit and ae-to-motion audit_motion.py",
|
||||
"mediaClosure": "pass: Source Media/clip/footage/relative TIFF and media SHA256",
|
||||
"publications": "pass: exactly two copyright menus plus empty-name ./210",
|
||||
"copyrightBindings": "pass: complete Rig/widget trees unchanged from baseline, own audit resolves menus/publications",
|
||||
"selection": "pass: 0 selected content objects; project focus",
|
||||
"customArchiveStructure": "pass: exact inner bytes, own class/schema/values; Foundation outer secure decode",
|
||||
"customHostInstantiation": "pass: both native hosts"
|
||||
},
|
||||
"semanticPreservation": {
|
||||
"inverseDiffPassed": true,
|
||||
"allowedChanges": [
|
||||
"description/version",
|
||||
"46 native functional publications replaced by custom210",
|
||||
"custom210 own state archive",
|
||||
"observed scalar/point/string hidden flags",
|
||||
"publish OSC value 1 to 0"
|
||||
],
|
||||
"otherStructureAndLegacyValuesUnchanged": true
|
||||
},
|
||||
"reproducibleCodec": {
|
||||
"script": "../../scripts/inspector-template.py",
|
||||
"commands": [
|
||||
"python3 scripts/inspector-template.py audit \"Templates/大鱼人像美化/大鱼人像美化.moef\"",
|
||||
"python3 scripts/inspector-template.py encode --inner build/ui-analysis/PGInspectorState-default-inner.bplist --text-output build/ui-analysis/panel-value.txt"
|
||||
],
|
||||
"ownRoundtrip": "pass",
|
||||
"malformedOrVendorArchiveRejectionCases": 5,
|
||||
"foundationOuterSecureDecode": "pass",
|
||||
"innerClassDecode": "model generates own archive; structure decoded and exact generated bytes preserved; native host instantiation pending user"
|
||||
},
|
||||
"automatedInspectorValidation": {
|
||||
"scope": "offscreen AppKit inspector, real production controller and mocked host callbacks; no Motion/FCP UI",
|
||||
"fullChecks": 1551,
|
||||
"counts": {
|
||||
"beauty": 49,
|
||||
"face": 116,
|
||||
"thinChinJawShort": 796,
|
||||
"hostAdapterASan": 335,
|
||||
"oscASan": 98,
|
||||
"inspectorASan": 157
|
||||
},
|
||||
"nativeVisualQA": "See currentHostValidation and docs/verification/v1.1.2/native-qa.json",
|
||||
"manualAnalysisUI": "removed; legacy IDs hidden and buttons disabled",
|
||||
"renderMode": "realtime automatic follow; stored manual geometry never used",
|
||||
"templatePublishedEntries": 3,
|
||||
"offscreenWidths": [
|
||||
280,
|
||||
320,
|
||||
380,
|
||||
520
|
||||
],
|
||||
"independentViewChecks": 27,
|
||||
"fixedRegressions": [
|
||||
"shape group bypass clears face-box overlay",
|
||||
"disabled legacy detail effects stay disabled on first edit",
|
||||
"legacy global smoothing remains enabled when its old local group was off",
|
||||
"new gesture reads current host after immediate undo",
|
||||
"pending synchronization is bound to CMTime",
|
||||
"same-time host change refreshes without waiting",
|
||||
"explicit numeric input is never skipped due to stale UI cache"
|
||||
],
|
||||
"archives": "Own PGInspectorState secure payload; Foundation outer and inner decode and Motion alphabet roundtrip tested"
|
||||
},
|
||||
"installation": {
|
||||
"version": "1.1.2",
|
||||
"build": "13",
|
||||
"status": "Release signed, notarized, stapled, installed; generated copies cleaned",
|
||||
"previousInstalledVersion": "1.1.1 (interim)",
|
||||
"sourceCommit": "a2e33e8e00a99b95dc29d67a819cb0897eda5756",
|
||||
"notarization": "Accepted 5f5ce378-8430-467b-ae97-048e6bc81573",
|
||||
"uniqueAppInScannedScope": true,
|
||||
"scopeReport": "../../docs/verification/v1.1.2/installed-verification.json"
|
||||
},
|
||||
"shortJawValidation": {
|
||||
"version": "1.1.2",
|
||||
"build": "13",
|
||||
"total": 1551,
|
||||
"counts": {
|
||||
"beauty": 49,
|
||||
"face": 116,
|
||||
"thinChinJawShort": 796,
|
||||
"hostAdapterASan": 335,
|
||||
"oscASan": 98,
|
||||
"inspectorASan": 157
|
||||
},
|
||||
"allPassed": true,
|
||||
"extraRealFramesASan": {
|
||||
"checks": 886,
|
||||
"includedRepeatedBaseChecks": true,
|
||||
"addedToTotal": false
|
||||
},
|
||||
"originCombinations": 28,
|
||||
"originTagsExactlyByteIdentical": true,
|
||||
"maxByteDifferenceVsIndependentUprightReference": 2,
|
||||
"meanByteDifferenceLimit": 0.005,
|
||||
"nativeCombinedBuildQA": "pending"
|
||||
}
|
||||
}
|
||||
二进制文件未显示。
|
之后 宽度: | 高度: | 大小: 444 KiB |
二进制文件未显示。
|
之后 宽度: | 高度: | 大小: 44 KiB |
+55
-91
@@ -46,7 +46,7 @@
|
||||
<flags>1</flags>
|
||||
</template>
|
||||
<build />
|
||||
<description>大鱼人像美化 1.0.6:人像磨皮、肤色、脸型、五官保护和三目标人脸跟踪。模板中频磨皮默认为50%。</description>
|
||||
<description>大鱼人像美化 1.1.2:磨皮、美白、瘦脸三卡片自定义面板,自动选择主体并实时跟随。模板局部磨皮默认为50%。</description>
|
||||
<scene>
|
||||
<sceneSettings>
|
||||
<width>1920</width>
|
||||
@@ -82,52 +82,7 @@
|
||||
<version>2</version>
|
||||
<target object="510000011" channel="./2/100" name="FCP 版本作者→" />
|
||||
<target object="510000012" channel="./2/100" name="更多模板浏览器访问→" />
|
||||
<target object="510000005" channel="./100/70" name="启用磨皮" />
|
||||
<target object="510000005" channel="./100/1" name="磨皮强度" />
|
||||
<target object="510000005" channel="./100/12" name="中频磨皮" />
|
||||
<target object="510000005" channel="./100/3" name="磨皮半径" />
|
||||
<target object="510000005" channel="./100/30" name="保护五官" />
|
||||
<target object="510000005" channel="./101/71" name="启用肤色" />
|
||||
<target object="510000005" channel="./101/42" name="肤色风格" />
|
||||
<target object="510000005" channel="./101/2" name="肤色提亮" />
|
||||
<target object="510000005" channel="./101/14" name="肤色色相" />
|
||||
<target object="510000005" channel="./101/15" name="肤色饱和度" />
|
||||
<target object="510000005" channel="./101/41" name="均匀肤色" />
|
||||
<target object="510000005" channel="./101/31" name="牙齿美白" />
|
||||
<target object="510000005" channel="./101/32" name="眼睛提亮" />
|
||||
<target object="510000005" channel="./102/73" name="启用细节" />
|
||||
<target object="510000005" channel="./102/17" name="高频细节平滑" />
|
||||
<target object="510000005" channel="./102/4" name="锐化" />
|
||||
<target object="510000005" channel="./103/72" name="启用脸型" />
|
||||
<target object="510000005" channel="./103/18" name="瘦脸" />
|
||||
<target object="510000005" channel="./103/33" name="大眼" />
|
||||
<target object="510000005" channel="./103/22" name="短脸" />
|
||||
<target object="510000005" channel="./103/23" name="窄脸" />
|
||||
<target object="510000005" channel="./103/24" name="下巴" />
|
||||
<target object="510000005" channel="./103/25" name="V 脸" />
|
||||
<target object="510000005" channel="./103/26" name="下颌" />
|
||||
<target object="510000005" channel="./103/27" name="颧骨" />
|
||||
<target object="510000005" channel="./103/28" name="小头" />
|
||||
<target object="510000005" channel="./103/29" name="小头范围" />
|
||||
<target object="510000005" channel="./104/20" name="只处理选中人脸" />
|
||||
<target object="510000005" channel="./104/21" name="选脸位置" />
|
||||
<target object="510000005" channel="./104/39" name="多人高速模式" />
|
||||
<target object="510000005" channel="./104/40" name="防跳脸范围" />
|
||||
<target object="510000005" channel="./104/45" name="使用跟踪数据" />
|
||||
<target object="510000005" channel="./104/46" name="跟踪目标" />
|
||||
<target object="510000005" channel="./104/50" name="向前分析" />
|
||||
<target object="510000005" channel="./104/51" name="向后分析" />
|
||||
<target object="510000005" channel="./104/52" name="清除当前目标" />
|
||||
<target object="510000005" channel="./104/53" name="跟踪状态" />
|
||||
<target object="510000005" channel="./104/19" name="显示人脸框" />
|
||||
<target object="510000005" channel="./104/35" name="显示跟踪框" />
|
||||
<target object="510000005" channel="./105/7" name="蒙版阈值" />
|
||||
<target object="510000005" channel="./105/8" name="蒙版柔度" />
|
||||
<target object="510000005" channel="./105/9" name="边缘保护" />
|
||||
<target object="510000005" channel="./105/10" name="预览蒙版" />
|
||||
<target object="510000005" channel="./105/43" name="反转蒙版" />
|
||||
<target object="510000005" channel="./105/44" name="蒙版通道" />
|
||||
<target object="510000005" channel="./105/5" name="整体混合" />
|
||||
<target object="510000005" channel="./210" name="" />
|
||||
</publishSettings>
|
||||
<currentFrame>0 1 1 0</currentFrame>
|
||||
<currentObject>510000001</currentObject>
|
||||
@@ -298,72 +253,81 @@
|
||||
<parameter name="Width" id="313" flags="8589934610" default="1" value="1920" />
|
||||
<parameter name="Height" id="314" flags="8589934610" default="1" value="1080" />
|
||||
</parameter>
|
||||
<filter name="大鱼人像美化" id="510000005" factoryID="6" pluginUUID="FB14B0E6-59B2-4836-916F-CD0C559E9241" pluginVersion="1.0.6" pluginName="大鱼人像美化" pluginDynamicParams="0">
|
||||
<filter name="大鱼人像美化" id="510000005" factoryID="6" pluginUUID="FB14B0E6-59B2-4836-916F-CD0C559E9241" pluginVersion="1.1.2" pluginName="大鱼人像美化" pluginDynamicParams="0">
|
||||
<timing in="0 1 1 0" out="299 30 1 0" offset="0 1 1 0" />
|
||||
<baseFlags>8589934608</baseFlags>
|
||||
<parameter name="" id="210" flags="12901679104">
|
||||
<numberOfKeypoints>1</numberOfKeypoints>
|
||||
<keypoint>
|
||||
<time>0 0 0 0</time>
|
||||
<value />
|
||||
</keypoint>
|
||||
<defaultVal>********-4QeMb-gOLBoA11I*E61-*I4-klM75NZQbBdPqtN743mMqVdRaJmJ0FoPr-M74xWOaJXR5AG**44c3wE1otHGqJtNKF-QaBcOLNZQh660Ec9L2FVR437QolZNq3XSJwE1o7gOKtYF43oMIxWOaJXR6*0U*4X1EsDJGFiRKlgHl21dq7kP4ZnR1*kp*20*kE3-UQ8K0FqNL7nOKxiKGFVQaBcOLNZQZEYR4xkK0FjMadZMrFn2U*-Ve-T2*xCIohZSKJYEL7XO4ZqNL9F0*ZPErJnR4xh62FVR44**OwE8EgA3pZOKplRLZxUMK7XN4JaNqVdOahgPKtjQ53mQrFpRbRsSLdvUMG5JGFiRKlgpEoC1l*F2VAI3FNKRa3gRKJnJWFXP43nQpNZN4ZoNKFKQqBcNKpVK4FdQq3WP4JYU*8*8*UE*M*aolUN1VctK3RCImtfNLZnKYtH9axWOaJXR5Cj2-sP5-oS5m*V6WAY7GMb80Ye8mkh9WwkAH6nB1IqBnW**s*2U*K*-c*5U*W*0M*8U*i*16*BU*u*1s*EU-4*2c*HU-G*3M*KU-S*46*NU-e*4s*QU-q*5c*TU00j2-suCnguCncuCXd1CncuCXcuCXcuCncvCXcuCXdJCXe*6M*WU08*6M*WU04*6M*VU04*6s*WU04*6M*VU04*6M*VU04*6M*WU04*6c*VU04*6M*VU04*76*VU04*7F*d2-sEFl*P2*IE6l*M2*6E6-*c22ME2V*R2-cE1l*22-QE1-*e22UE5l*-20QE5-*F2-YE1V*1202E3WA**********0Azw********0AzzAnAk****0B*6********B7wTLtzKWFXP43nQqtVPKJM74BgMLBnNLBQHZB2OKBoOKxiML7tcbu*K2tHHq7eNKBooVYCUcCUU0TGT5q3VZJCIpBZR883UB7wTMW7Ll*EI2R7PbBkNKBoPr7HR43oNO88U3wE23-5GKtnQ4JXR4xmIrFVR4I*0**F*-c*7**d*16*Bk-7*2k*K*-O*6M*X*0L*7s*dE0g*9A*j*0y*A**kE11*AI*n*1I*Bw-**20*EE--U26*Ec-1*2C*F*-2U2I*FM-4*2O*Fk-5U2U*G6-7*2a*GU-8U2g*Gs-A*2m*HE-BU2s*Hc-D*3R*Jw-ME3X*KI-Nk3d*Kg-PE3j*L2-Qk3p*LQ-SE3v*Lo-Tk4-*MA-VE45*MY-Wk4B*Mw-YE4H*NI-Zk4N*Ng-bE4T*O2-ck4Z*OQ-eE4f*Oo-fk4l*PA-hE4r*PY-ik4x*Pw-kE51*QI-lk57*Qg-nE5D*R2-ok5J*RQ-s*5d*T6-yk6**Ug03*6V*WE09E6m*XA0BE6u*Y*0Ek76*Zg0LU********6-*********6g*******************7l0**6*-2*4U*Y*0Y*AU*r*2Y*HU-P*4o*Pk-l*5I*SkEa*********U2*********2*******************-0Q*</defaultVal>
|
||||
<dataValue>********-4QeMb-gOLBoA11I*E61-*I4-klM75NZQbBdPqtN743mMqVdRaJmJ0FoPr-M74xWOaJXR5AG**44c3wE1otHGqJtNKF-QaBcOLNZQh660Ec9L2FVR437QolZNq3XSJwE1o7gOKtYF43oMIxWOaJXR6*0U*4X1EsDJGFiRKlgHl21dq7kP4ZnR1*kp*20*kE3-UQ8K0FqNL7nOKxiKGFVQaBcOLNZQZEYR4xkK0FjMadZMrFn2U*-Ve-T2*xCIohZSKJYEL7XO4ZqNL9F0*ZPErJnR4xh62FVR44**OwE8EgA3pZOKplRLZxUMK7XN4JaNqVdOahgPKtjQ53mQrFpRbRsSLdvUMG5JGFiRKlgpEoC1l*F2VAI3FNKRa3gRKJnJWFXP43nQpNZN4ZoNKFKQqBcNKpVK4FdQq3WP4JYU*8*8*UE*M*aolUN1VctK3RCImtfNLZnKYtH9axWOaJXR5Cj2-sP5-oS5m*V6WAY7GMb80Ye8mkh9WwkAH6nB1IqBnW**s*2U*K*-c*5U*W*0M*8U*i*16*BU*u*1s*EU-4*2c*HU-G*3M*KU-S*46*NU-e*4s*QU-q*5c*TU00j2-suCnguCncuCXd1CncuCXcuCXcuCncvCXcuCXdJCXe*6M*WU08*6M*WU04*6M*VU04*6s*WU04*6M*VU04*6M*VU04*6M*WU04*6c*VU04*6M*VU04*76*VU04*7F*d2-sEFl*P2*IE6l*M2*6E6-*c22ME2V*R2-cE1l*22-QE1-*e22UE5l*-20QE5-*F2-YE1V*1202E3WA**********0Azw********0AzzAnAk****0B*6********B7wTLtzKWFXP43nQqtVPKJM74BgMLBnNLBQHZB2OKBoOKxiML7tcbu*K2tHHq7eNKBooVYCUcCUU0TGT5q3VZJCIpBZR883UB7wTMW7Ll*EI2R7PbBkNKBoPr7HR43oNO88U3wE23-5GKtnQ4JXR4xmIrFVR4I*0**F*-c*7**d*16*Bk-7*2k*K*-O*6M*X*0L*7s*dE0g*9A*j*0y*A**kE11*AI*n*1I*Bw-**20*EE--U26*Ec-1*2C*F*-2U2I*FM-4*2O*Fk-5U2U*G6-7*2a*GU-8U2g*Gs-A*2m*HE-BU2s*Hc-D*3R*Jw-ME3X*KI-Nk3d*Kg-PE3j*L2-Qk3p*LQ-SE3v*Lo-Tk4-*MA-VE45*MY-Wk4B*Mw-YE4H*NI-Zk4N*Ng-bE4T*O2-ck4Z*OQ-eE4f*Oo-fk4l*PA-hE4r*PY-ik4x*Pw-kE51*QI-lk57*Qg-nE5D*R2-ok5J*RQ-s*5d*T6-yk6**Ug03*6V*WE09E6m*XA0BE6u*Y*0Ek76*Zg0LU********6-*********6g*******************7l0**6*-2*4U*Y*0Y*AU*r*2Y*HU-P*4o*Pk-l*5I*SkEa*********U2*********2*******************-0Q*</dataValue>
|
||||
</parameter>
|
||||
<parameter name="磨皮" id="100" flags="8589938704">
|
||||
<parameter name="启用磨皮" id="70" flags="12901679120" default="1" value="1" />
|
||||
<parameter name="磨皮强度" id="1" flags="12901679120" default="1" value="1" />
|
||||
<parameter name="中频磨皮" id="12" flags="12901679120" default="0" value="0.5" />
|
||||
<parameter name="磨皮半径" id="3" flags="12901679120" default="8" value="8" />
|
||||
<parameter name="保护五官" id="30" flags="12901679120" default="1" value="1" />
|
||||
<parameter name="启用磨皮" id="70" flags="12905938976" default="1" value="1" />
|
||||
<parameter name="磨皮强度" id="1" flags="12905938976" default="1" value="1" />
|
||||
<parameter name="中频磨皮" id="12" flags="12905938976" default="0" value="0.5" />
|
||||
<parameter name="磨皮半径" id="3" flags="12905938976" default="8" value="8" />
|
||||
<parameter name="保护五官" id="30" flags="12905938976" default="1" value="1" />
|
||||
</parameter>
|
||||
<parameter name="肤色" id="101" flags="8589938704">
|
||||
<parameter name="启用肤色" id="71" flags="12901679120" default="1" value="1" />
|
||||
<parameter name="启用肤色" id="71" flags="12905938976" default="1" value="1" />
|
||||
<parameter name="肤色风格" id="42" flags="12901744656" default="0" value="0" />
|
||||
<parameter name="肤色提亮" id="2" flags="12901679120" default="0" value="0" />
|
||||
<parameter name="肤色色相" id="14" flags="12901679120" default="0" value="0" />
|
||||
<parameter name="肤色饱和度" id="15" flags="12901679120" default="0" value="0" />
|
||||
<parameter name="均匀肤色" id="41" flags="12901679120" default="0" value="0" />
|
||||
<parameter name="牙齿美白" id="31" flags="12901679120" default="0" value="0" />
|
||||
<parameter name="眼睛提亮" id="32" flags="12901679120" default="0" value="0" />
|
||||
<parameter name="肤色提亮" id="2" flags="12905938976" default="0" value="0" />
|
||||
<parameter name="肤色色相" id="14" flags="12905938976" default="0" value="0" />
|
||||
<parameter name="肤色饱和度" id="15" flags="12905938976" default="0" value="0" />
|
||||
<parameter name="均匀肤色" id="41" flags="12905938976" default="0" value="0" />
|
||||
<parameter name="牙齿美白" id="31" flags="12905938976" default="0" value="0" />
|
||||
<parameter name="眼睛提亮" id="32" flags="12905938976" default="0" value="0" />
|
||||
</parameter>
|
||||
<parameter name="细节" id="102" flags="8589938704">
|
||||
<parameter name="启用细节" id="73" flags="12901679120" default="1" value="1" />
|
||||
<parameter name="高频细节平滑" id="17" flags="12901679120" default="0" value="0" />
|
||||
<parameter name="锐化" id="4" flags="12901679120" default="0" value="0" />
|
||||
<parameter name="启用细节" id="73" flags="12905938976" default="1" value="1" />
|
||||
<parameter name="高频细节平滑" id="17" flags="12905938976" default="0" value="0" />
|
||||
<parameter name="锐化" id="4" flags="12905938976" default="0" value="0" />
|
||||
</parameter>
|
||||
<parameter name="脸型" id="103" flags="8589938704">
|
||||
<parameter name="启用脸型" id="72" flags="12901679120" default="1" value="1" />
|
||||
<parameter name="瘦脸" id="18" flags="12901679120" default="0" value="0" />
|
||||
<parameter name="大眼" id="33" flags="12901679120" default="0" value="0" />
|
||||
<parameter name="短脸" id="22" flags="12901679120" default="0" value="0" />
|
||||
<parameter name="窄脸" id="23" flags="12901679120" default="0" value="0" />
|
||||
<parameter name="下巴" id="24" flags="12901679120" default="0" value="0" />
|
||||
<parameter name="V 脸" id="25" flags="12901679120" default="0" value="0" />
|
||||
<parameter name="下颌" id="26" flags="12901679120" default="0" value="0" />
|
||||
<parameter name="颧骨" id="27" flags="12901679120" default="0" value="0" />
|
||||
<parameter name="小头" id="28" flags="12901679120" default="0" value="0" />
|
||||
<parameter name="小头范围" id="29" flags="12901679120" default="0" value="0" />
|
||||
<parameter name="启用脸型" id="72" flags="12905938976" default="1" value="1" />
|
||||
<parameter name="瘦脸" id="18" flags="12905938976" default="0" value="0" />
|
||||
<parameter name="大眼" id="33" flags="12905938976" default="0" value="0" />
|
||||
<parameter name="短脸" id="22" flags="12905938976" default="0" value="0" />
|
||||
<parameter name="窄脸" id="23" flags="12905938976" default="0" value="0" />
|
||||
<parameter name="下巴" id="24" flags="12905938976" default="0" value="0" />
|
||||
<parameter name="V 脸" id="25" flags="12905938976" default="0" value="0" />
|
||||
<parameter name="下颌" id="26" flags="12905938976" default="0" value="0" />
|
||||
<parameter name="颧骨" id="27" flags="12905938976" default="0" value="0" />
|
||||
<parameter name="小头" id="28" flags="12905938976" default="0" value="0" />
|
||||
<parameter name="小头范围" id="29" flags="12905938976" default="0" value="0" />
|
||||
</parameter>
|
||||
<parameter name="人脸选择与跟踪" id="104" flags="8589938704">
|
||||
<parameter name="只处理选中人脸" id="20" flags="12901679120" default="0" value="0" />
|
||||
<parameter name="选脸位置" id="21" flags="12901683200">
|
||||
<parameter name="只处理选中人脸" id="20" flags="12905938976" default="0" value="0" />
|
||||
<parameter name="选脸位置" id="21" flags="12905943072">
|
||||
<foldFlags>15</foldFlags>
|
||||
<parameter name="X" id="1" flags="12901679120" default="0.5" value="0.5" />
|
||||
<parameter name="Y" id="2" flags="12901679120" default="0.5" value="0.5" />
|
||||
<parameter name="X" id="1" flags="12905938976" default="0.5" value="0.5" />
|
||||
<parameter name="Y" id="2" flags="12905938976" default="0.5" value="0.5" />
|
||||
</parameter>
|
||||
<parameter name="多人高速模式" id="39" flags="12901679120" default="0" value="0" />
|
||||
<parameter name="防跳脸范围" id="40" flags="12901679120" default="1.8" value="1.8" />
|
||||
<parameter name="使用跟踪数据" id="45" flags="12901679120" default="0" value="0" />
|
||||
<parameter name="多人高速模式" id="39" flags="12905938976" default="0" value="0" />
|
||||
<parameter name="防跳脸范围" id="40" flags="12905938976" default="1.8" value="1.8" />
|
||||
<parameter name="使用跟踪数据" id="45" flags="12905938976" default="0" value="0" />
|
||||
<parameter name="跟踪目标" id="46" flags="12901744656" default="0" value="0" />
|
||||
<parameter name="向前分析" id="50" />
|
||||
<parameter name="向后分析" id="51" />
|
||||
<parameter name="清除当前目标" id="52" />
|
||||
<parameter name="跟踪状态" id="53" flags="12889161760">
|
||||
<parameter name="跟踪状态" id="53" flags="12905938976">
|
||||
<text>选择画面中的人脸后开始分析</text>
|
||||
</parameter>
|
||||
<parameter name="显示人脸框" id="19" flags="12901679120" default="0" value="0" />
|
||||
<parameter name="显示跟踪框" id="35" flags="12901679120" default="0" value="0" />
|
||||
<parameter name="显示人脸框" id="19" flags="12905938976" default="0" value="0" />
|
||||
<parameter name="显示跟踪框" id="35" flags="12905938976" default="0" value="0" />
|
||||
</parameter>
|
||||
<parameter name="蒙版与输出" id="105" flags="8589938704">
|
||||
<parameter name="蒙版阈值" id="7" flags="12901679120" default="148" value="148" />
|
||||
<parameter name="蒙版柔度" id="8" flags="12901679120" default="0.6" value="0.6" />
|
||||
<parameter name="边缘保护" id="9" flags="12901679120" default="0.4" value="0.4" />
|
||||
<parameter name="预览蒙版" id="10" flags="12901679120" default="0" value="0" />
|
||||
<parameter name="反转蒙版" id="43" flags="12901679120" default="0" value="0" />
|
||||
<parameter name="蒙版阈值" id="7" flags="12905938976" default="148" value="148" />
|
||||
<parameter name="蒙版柔度" id="8" flags="12905938976" default="0.6" value="0.6" />
|
||||
<parameter name="边缘保护" id="9" flags="12905938976" default="0.4" value="0.4" />
|
||||
<parameter name="预览蒙版" id="10" flags="12905938976" default="0" value="0" />
|
||||
<parameter name="反转蒙版" id="43" flags="12905938976" default="0" value="0" />
|
||||
<parameter name="蒙版通道" id="44" flags="12901744656" default="0" value="0" />
|
||||
<parameter name="整体混合" id="5" flags="12901679120" default="1" value="1" />
|
||||
<parameter name="整体混合" id="5" flags="12905938976" default="1" value="1" />
|
||||
</parameter>
|
||||
<parameter name="分析数据" id="60" flags="12905938976">
|
||||
<text />
|
||||
@@ -377,7 +341,7 @@
|
||||
<parameter name="混合" id="10001" flags="12884901888" default="1" value="1" />
|
||||
<parameter name="Flip" id="10002" flags="12889161760" default="0" value="0" />
|
||||
<parameter name="Input Points" id="10003" flags="12889161760" default="1" value="1" />
|
||||
<parameter name="发布 OSC" id="10005" flags="12901679104" default="0" value="1" />
|
||||
<parameter name="发布 OSC" id="10005" flags="12901679104" default="0" value="0" />
|
||||
</filter>
|
||||
</scenenode>
|
||||
<aspectRatio>1</aspectRatio>
|
||||
|
||||
@@ -63,11 +63,13 @@
|
||||
"/Localized: Plugin/InfoPlist.strings",
|
||||
"/Localized: Plugin/Localizable.strings",
|
||||
Plugin/beatygrilPlugIn.m,
|
||||
Plugin/Info.plist,
|
||||
Plugin/main.m,
|
||||
Plugin/PGBeautyRenderer.m,
|
||||
Plugin/PGFaceProcessor.m,
|
||||
Plugin/PGFaceSelectOSC.m,
|
||||
Plugin/Info.plist,
|
||||
Plugin/main.m,
|
||||
Plugin/PGInspectorState.m,
|
||||
Plugin/PGInspectorView.m,
|
||||
"Wrapper Application/Info.plist",
|
||||
);
|
||||
target = 6945FC8A3088B7220013E108 /* Wrapper Application */;
|
||||
@@ -80,6 +82,7 @@
|
||||
"/Localized: Wrapper Application/MainMenu.xib",
|
||||
Plugin/Info.plist,
|
||||
"Wrapper Application/AppDelegate.m",
|
||||
"Wrapper Application/Resources/LauncherInfo.png",
|
||||
"Wrapper Application/Info.plist",
|
||||
"Wrapper Application/main.m",
|
||||
);
|
||||
@@ -306,11 +309,11 @@
|
||||
6945FCCD3088B7220013E108 /* Debug configuration for PBXNativeTarget "XPC Service" */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO;
|
||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO;
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
DEVELOPMENT_TEAM = B3W25FX5H5;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
INFOPLIST_FILE = beatygril/Plugin/Info.plist;
|
||||
@@ -329,11 +332,11 @@
|
||||
6945FCCE3088B7220013E108 /* Release configuration for PBXNativeTarget "XPC Service" */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO;
|
||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO;
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
DEVELOPMENT_TEAM = B3W25FX5H5;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
INFOPLIST_FILE = beatygril/Plugin/Info.plist;
|
||||
@@ -353,11 +356,12 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = "beatygril/Wrapper Application/SandboxEntitlements.entitlements";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO;
|
||||
OTHER_LDFLAGS = ("$(inherited)", "-framework", CoreServices, );
|
||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO;
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
DEVELOPMENT_TEAM = B3W25FX5H5;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
INFOPLIST_FILE = "beatygril/Wrapper Application/Info.plist";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.dayu.beatygril;
|
||||
@@ -370,11 +374,12 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = "beatygril/Wrapper Application/SandboxEntitlements.entitlements";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO;
|
||||
OTHER_LDFLAGS = ("$(inherited)", "-framework", CoreServices, );
|
||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO;
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
DEVELOPMENT_TEAM = B3W25FX5H5;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
INFOPLIST_FILE = "beatygril/Wrapper Application/Info.plist";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.dayu.beatygril;
|
||||
@@ -387,9 +392,11 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ADDITIONAL_SDKS = /Library/Developer/SDKs/FxPlug.sdk;
|
||||
ARCHS = "arm64 x86_64";
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ARCHS = (
|
||||
arm64,
|
||||
x86_64,
|
||||
);
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
@@ -448,6 +455,7 @@
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_VERSION = 5;
|
||||
};
|
||||
@@ -457,9 +465,11 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ADDITIONAL_SDKS = /Library/Developer/SDKs/FxPlug.sdk;
|
||||
ARCHS = "arm64 x86_64";
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ARCHS = (
|
||||
arm64,
|
||||
x86_64,
|
||||
);
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
@@ -512,6 +522,7 @@
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_VERSION = 5;
|
||||
};
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0.6</string>
|
||||
<string>1.2.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>8</string>
|
||||
<string>14</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string></string>
|
||||
<key>PlugInKit</key>
|
||||
@@ -72,7 +72,7 @@
|
||||
<key>uuid</key>
|
||||
<string>FB14B0E6-59B2-4836-916F-CD0C559E9241</string>
|
||||
<key>version</key>
|
||||
<string>1.0.6</string>
|
||||
<string>1.2.0</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>className</key>
|
||||
@@ -84,7 +84,7 @@
|
||||
<key>uuid</key>
|
||||
<string>E0E432C8-3D71-4ABA-B006-E7F519E06C87</string>
|
||||
<key>version</key>
|
||||
<string>1.0.6</string>
|
||||
<string>1.2.0</string>
|
||||
<key>protocolNames</key>
|
||||
<array>
|
||||
<string>FxOnScreenControl</string>
|
||||
|
||||
@@ -14,6 +14,8 @@ static const uint8_t PGThinPins[17][2] = {
|
||||
{69,49},{65,49},{68,52},{66,52},{67,47}
|
||||
};
|
||||
static const uint8_t PGChinPins[3][2] = {{68,38},{67,37},{66,36}};
|
||||
// Origin and radius neighbour; all six directions point toward landmark 29.
|
||||
static const uint8_t PGJawPins[6][2] = {{72,71},{62,63},{71,70},{63,64},{70,71},{64,63}};
|
||||
|
||||
@interface PGFaceFrame ()
|
||||
@property (nonatomic, copy, readwrite) NSArray<VNFaceObservation *> *faces;
|
||||
@@ -125,7 +127,8 @@ static NSDictionary *PGSanitizedGeometry(id value) {
|
||||
result[name] = points;
|
||||
}
|
||||
// Old archives and partially detected faces remain usable by the other
|
||||
// controls. Missing or malformed full topology disables local thin/chin pins.
|
||||
// controls. Missing or malformed full topology disables the recovered local
|
||||
// thin/chin/jaw/short fields without discarding otherwise usable old records.
|
||||
NSArray *points76 = PGSanitizedPoints76(value[@"points76"]);
|
||||
if (points76) result[@"points76"] = points76;
|
||||
return [result copy];
|
||||
@@ -344,10 +347,11 @@ static NSDictionary *PGStableGeometry(NSDictionary *current, NSDictionary *previ
|
||||
_lock = [[NSLock alloc] init];
|
||||
_realtime = [PGRealtimeFaceTracker new];
|
||||
_targets = [NSMutableArray arrayWithObjects:[NSMutableDictionary dictionary], [NSMutableDictionary dictionary], [NSMutableDictionary dictionary], nil];
|
||||
// Local thinning and chin use the recovered 17/3-pin coordinate math.
|
||||
// The other seven controls retain this project's analytic approximation.
|
||||
// Chin follows those analytic body controls and precedes the eye warp;
|
||||
// this combined order is not a reconstruction of the full reference chain.
|
||||
// Thin/chin/jaw/short reconstruct the recovered local coordinate math.
|
||||
// The other five controls retain this project's analytic approximation.
|
||||
// Recovered jaw -> chin -> short order follows the reference. The other
|
||||
// analytic body controls still precede jaw, so the full combined chain
|
||||
// (narrow/V/cheek/small-head/eyes) is not claimed reference-equivalent.
|
||||
// Each pin changes the sampling coordinate, with only one image resample.
|
||||
NSMutableString *source = [NSMutableString stringWithString:
|
||||
@"vec2 pgThinPin(vec2 q, vec4 control, float ratio) {"
|
||||
@@ -355,9 +359,24 @@ static NSDictionary *PGStableGeometry(NSDictionary *current, NSDictionary *previ
|
||||
" if(radius<=0.0001) return q;"
|
||||
" float weight=clamp(1.0-length(q-control.xy)/radius,0.0,1.0);"
|
||||
" return clamp(q-direction*ratio*weight,vec2(0.0),vec2(1.0)); }"
|
||||
" kernel vec2 pgFaceWarp(vec2 c, vec2 size, vec2 le, vec2 re, vec2 er, float thinRatio, float bigEye, float shortFace, float narrow, float chinRatio, float vFace, float jaw, float cheek, float smallHead, float headRange, vec2 imageOrigin, vec2 imageSize"];
|
||||
" vec2 pgJawPin(vec2 q, vec4 edge, vec2 anchor, float ratio) {"
|
||||
" float radius=length(edge.xy-edge.zw); if(radius<=0.0001) return q;"
|
||||
" float weight=clamp(1.0-length(q-edge.xy)/radius,0.0,1.0);"
|
||||
" return clamp(q-(anchor-edge.xy)*ratio*weight,vec2(0.0),vec2(1.0)); }"
|
||||
" vec2 pgShortEdge(vec2 q, vec2 a, vec2 b) {"
|
||||
" vec2 d=b-a; float squared=dot(d,d);"
|
||||
" float t=0.0; if(squared>0.000001) t=clamp(dot(q-a,d)/squared,0.0,1.0);"
|
||||
" float distance=length(q-(a+t*d)); float crossing=0.0;"
|
||||
" if((a.y>q.y && b.y<=q.y) || (b.y>q.y && a.y<=q.y)) {"
|
||||
" float dy=b.y-a.y; if(abs(dy)<=0.000001) dy=dy<0.0?-0.000001:0.000001;"
|
||||
" if(q.x<a.x+(b.x-a.x)*(q.y-a.y)/dy) crossing=1.0; }"
|
||||
" return vec2(distance,crossing); }"
|
||||
" kernel vec2 pgFaceWarp(vec2 c, vec2 size, vec2 le, vec2 re, vec2 er, float thinRatio, float bigEye, float shortRatio, float narrow, float chinRatio, float vFace, float jawRatio, float cheek, float smallHead, float headRange, vec2 imageOrigin, vec2 imageSize"];
|
||||
for (NSUInteger i=0; i<17; i++) [source appendFormat:@", vec4 pin%lu",(unsigned long)i];
|
||||
for (NSUInteger i=0; i<3; i++) [source appendFormat:@", vec4 chinPin%lu",(unsigned long)i];
|
||||
[source appendString:@", vec2 jawAnchor, vec2 shortAxis"];
|
||||
for (NSUInteger i=0; i<6; i++) [source appendFormat:@", vec4 jawPin%lu",(unsigned long)i];
|
||||
for (NSUInteger i=0; i<11; i++) [source appendFormat:@", vec2 shortPoint%lu",(unsigned long)i];
|
||||
[source appendString:@") { vec2 p=destCoord();"
|
||||
" if(thinRatio>0.000001) { vec2 q=(p-imageOrigin)/imageSize; q.y=1.0-q.y;"];
|
||||
for (NSUInteger i=0; i<17; i++) [source appendFormat:@" q=pgThinPin(q,pin%lu,thinRatio);",(unsigned long)i];
|
||||
@@ -370,11 +389,21 @@ static NSDictionary *PGStableGeometry(NSDictionary *current, NSDictionary *previ
|
||||
" float ch=(n.y-0.02)/0.19; float cheekBand = exp(-ch*ch)*body;"
|
||||
" float head = 1.0-smoothstep(0.38,0.75+0.32*headRange,length(n*vec2(0.9,0.65)));"
|
||||
" p += (p-c)*smallHead*0.18*head;"
|
||||
" p.x += size.x*n.x*(narrow*0.30*body + vFace*0.24*jawBand + jaw*0.20*jawBand + cheek*0.18*cheekBand)*sides;"
|
||||
" p.y += size.y*(-shortFace*0.16*lower - vFace*0.025*lower);"
|
||||
" p.x += size.x*n.x*(narrow*0.30*body + vFace*0.24*jawBand + cheek*0.18*cheekBand)*sides;"
|
||||
" p.y += size.y*(-vFace*0.025*lower);"
|
||||
" if(abs(jawRatio)>0.000001) { vec2 q=(p-imageOrigin)/imageSize; q.y=1.0-q.y;"];
|
||||
for (NSUInteger i=0; i<6; i++) [source appendFormat:@" q=pgJawPin(q,jawPin%lu,jawAnchor,jawRatio);",(unsigned long)i];
|
||||
[source appendString:@" q.y=1.0-q.y; p=imageOrigin+q*imageSize; }"
|
||||
" if(abs(chinRatio)>0.000001) { vec2 q=(p-imageOrigin)/imageSize; q.y=1.0-q.y;"];
|
||||
for (NSUInteger i=0; i<3; i++) [source appendFormat:@" q=pgThinPin(q,chinPin%lu,chinRatio);",(unsigned long)i];
|
||||
[source appendString:@" q.y=1.0-q.y; p=imageOrigin+q*imageSize; }"
|
||||
" if(abs(shortRatio)>0.000001 && length(shortAxis)>0.0001) {"
|
||||
" vec2 q=(p-imageOrigin)/imageSize; q.y=1.0-q.y;"
|
||||
" float nearest=10.0; float crossings=0.0;"];
|
||||
for (NSUInteger i=0; i<11; i++) [source appendFormat:@" { vec2 edge=pgShortEdge(q,shortPoint%lu,shortPoint%lu); nearest=min(nearest,edge.x); crossings+=edge.y; }",(unsigned long)i,(unsigned long)((i+1)%11)];
|
||||
[source appendString:@" float weight=mod(crossings,2.0)>0.5?1.0:clamp(1.0-nearest/length(shortAxis),0.0,1.0);"
|
||||
" q=clamp(q-shortAxis*shortRatio*weight,vec2(0.0),vec2(1.0));"
|
||||
" q.y=1.0-q.y; p=imageOrigin+q*imageSize; }"
|
||||
" vec2 dl = (p-le)/er; float wl = 1.0-smoothstep(0.0,1.0,length(dl));"
|
||||
" p -= (p-le)*bigEye*0.33*wl*wl;"
|
||||
" vec2 dr = (p-re)/er; float wr = 1.0-smoothstep(0.0,1.0,length(dr));"
|
||||
@@ -498,7 +527,7 @@ static NSDictionary *PGStableGeometry(NSDictionary *current, NSDictionary *previ
|
||||
for (NSDictionary *face in geometry) {
|
||||
CGRect box = PGArrayRect(face[@"box"]), pixels = PGToPixels(box, extent);
|
||||
NSArray *points76 = PGSanitizedPoints76(face[@"points76"]);
|
||||
float thinRatio = 0, chinRatio = 0;
|
||||
float thinRatio = 0, chinRatio = 0, jawRatio = 0, shortRatio = 0;
|
||||
if (points76) {
|
||||
double width = hypot(([points76[75][0] doubleValue]-[points76[59][0] doubleValue])*extent.size.width,
|
||||
([points76[75][1] doubleValue]-[points76[59][1] doubleValue])*extent.size.height);
|
||||
@@ -510,19 +539,21 @@ static NSDictionary *PGStableGeometry(NSDictionary *current, NSDictionary *previ
|
||||
thinRatio = PGClamp(s.faceThin,0,1)*.025*gain;
|
||||
float chin = PGClamp(s.faceChin,-1,1);
|
||||
chinRatio = chin*(chin>0 ? .04 : .02)*gain;
|
||||
jawRatio = PGClamp(s.faceJaw,0,1)*.05*gain;
|
||||
shortRatio = PGClamp(s.faceShort,-1,1)*.25*gain;
|
||||
}
|
||||
BOOL otherControls = s.faceBigEye || s.faceShort || s.faceNarrow || s.faceV || s.faceJaw || s.faceCheekbone || s.faceSmallHead;
|
||||
// No full topology means no guessed bbox thin/chin deformation. Legacy
|
||||
BOOL otherControls = s.faceBigEye || s.faceNarrow || s.faceV || s.faceCheekbone || s.faceSmallHead;
|
||||
// No full topology means no guessed bbox thin/chin/jaw/short deformation. Legacy
|
||||
// archives using only those controls remain exact image/mask pass-through.
|
||||
if (thinRatio <= .000001 && fabsf(chinRatio) <= .000001 && !otherControls) continue;
|
||||
if (thinRatio <= .000001 && fabsf(chinRatio) <= .000001 && fabsf(jawRatio) <= .000001 && fabsf(shortRatio) <= .000001 && !otherControls) continue;
|
||||
CGRect le = PGRegionBounds(face, @"leftEye", PGInFace(box, 0.16, 0.53, 0.25, 0.15));
|
||||
CGRect re = PGRegionBounds(face, @"rightEye", PGInFace(box, 0.59, 0.53, 0.25, 0.15));
|
||||
CGPoint left = PGCenter(PGToPixels(le, extent)), right = PGCenter(PGToPixels(re, extent));
|
||||
NSMutableArray *arguments = [@[PGVector(PGCenter(pixels)), [CIVector vectorWithX:pixels.size.width Y:pixels.size.height],
|
||||
PGVector(left), PGVector(right), [CIVector vectorWithX:MAX(2, pixels.size.width * 0.24) Y:MAX(2, pixels.size.height * 0.17)],
|
||||
@(thinRatio), @(PGClamp(s.faceBigEye, 0, 1)), @(PGClamp(s.faceShort, -1, 1)),
|
||||
@(thinRatio), @(PGClamp(s.faceBigEye, 0, 1)), @(shortRatio),
|
||||
@(PGClamp(s.faceNarrow, -1, 1)), @(chinRatio), @(PGClamp(s.faceV, 0, 1)),
|
||||
@(PGClamp(s.faceJaw, 0, 1)), @(PGClamp(s.faceCheekbone, 0, 1)), @(PGClamp(s.faceSmallHead, 0, 1)), @(PGClamp(s.faceSmallHeadRange, 0, 1)),
|
||||
@(jawRatio), @(PGClamp(s.faceCheekbone, 0, 1)), @(PGClamp(s.faceSmallHead, 0, 1)), @(PGClamp(s.faceSmallHeadRange, 0, 1)),
|
||||
[CIVector vectorWithX:extent.origin.x Y:extent.origin.y], [CIVector vectorWithX:extent.size.width Y:extent.size.height]] mutableCopy];
|
||||
for (NSUInteger i=0; i<17; i++) {
|
||||
double ox=0,oy=0,ax=0,ay=0;
|
||||
@@ -542,6 +573,23 @@ static NSDictionary *PGStableGeometry(NSDictionary *current, NSDictionary *previ
|
||||
}
|
||||
[arguments addObject:[CIVector vectorWithX:ox Y:oy Z:ax W:ay]];
|
||||
}
|
||||
CGPoint anchor=CGPointZero,axis=CGPointZero;
|
||||
if(points76){
|
||||
anchor=CGPointMake([points76[29][0]doubleValue],1-[points76[29][1]doubleValue]);
|
||||
axis=CGPointMake([points76[46][0]doubleValue]-[points76[48][0]doubleValue],
|
||||
[points76[48][1]doubleValue]-[points76[46][1]doubleValue]);
|
||||
}
|
||||
[arguments addObject:PGVector(anchor)];[arguments addObject:PGVector(axis)];
|
||||
for(NSUInteger i=0;i<6;i++){
|
||||
double ox=0,oy=0,nx=0,ny=0;
|
||||
if(points76){NSArray *origin=points76[PGJawPins[i][0]],*neighbour=points76[PGJawPins[i][1]];
|
||||
ox=[origin[0]doubleValue];oy=1-[origin[1]doubleValue];nx=[neighbour[0]doubleValue];ny=1-[neighbour[1]doubleValue];}
|
||||
[arguments addObject:[CIVector vectorWithX:ox Y:oy Z:nx W:ny]];
|
||||
}
|
||||
for(NSUInteger i=62;i<=72;i++){
|
||||
CGPoint point=CGPointZero;if(points76)point=CGPointMake([points76[i][0]doubleValue],1-[points76[i][1]doubleValue]);
|
||||
[arguments addObject:PGVector(point)];
|
||||
}
|
||||
CIImage *input = [result imageByClampingToExtent];
|
||||
result = [_warp applyWithExtent:extent roiCallback:^CGRect(int index, CGRect rect) { (void)index; (void)rect; return extent; } inputImage:input arguments:arguments];
|
||||
if (!result) return nil;
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <FxPlug/FxPlugSDK.h>
|
||||
#import "PGBeautyTypes.h"
|
||||
#import "PGParameterIDs.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// The descriptor list is shared by UI, persistence and render settings.
|
||||
/// Keys: id, name, section (smooth/color/shape/more), kind
|
||||
/// (slider/toggle/preset/master), min, max, default, format, bypass.
|
||||
FOUNDATION_EXPORT NSArray<NSDictionary *> *PGInspectorControls(void);
|
||||
FOUNDATION_EXPORT NSDictionary * _Nullable PGInspectorDescriptor(UInt32 identifier);
|
||||
|
||||
@interface PGInspectorState : NSObject <NSSecureCoding, NSCopying, FxCustomParameterInterpolation_v2>
|
||||
@property (nonatomic, copy, readonly) NSDictionary<NSNumber *, NSNumber *> *values;
|
||||
@property (nonatomic, copy, readonly) NSSet<NSNumber *> *disabledParameters;
|
||||
/// An untouched panel must not replace parameters from an existing document.
|
||||
@property (nonatomic, readonly) BOOL hasEdits;
|
||||
+ (instancetype)stateWithValues:(NSDictionary *)values disabledParameters:(NSSet *)disabled hasEdits:(BOOL)edited;
|
||||
- (double)valueForParameter:(UInt32)identifier;
|
||||
- (BOOL)isParameterEnabled:(UInt32)identifier;
|
||||
@end
|
||||
|
||||
FOUNDATION_EXPORT PGBeautySettings PGSettingsWithInspectorState(PGBeautySettings settings, PGInspectorState * _Nullable state);
|
||||
|
||||
/// All arbitrary-time FxPlug parameter access is bracketed by host actions.
|
||||
/// The published custom object owns the full UI state, so FCP save/undo does
|
||||
/// not depend on unpublished parameters being mirrored by the template.
|
||||
@interface PGInspectorController : NSObject
|
||||
@property (nonatomic, strong, readonly) PGInspectorState *state;
|
||||
@property (nonatomic, readonly) BOOL available;
|
||||
@property (nonatomic, copy, readonly) NSString *statusMessage;
|
||||
/// The actual custom inspector view identifies the action to the native host.
|
||||
@property (nonatomic, weak, nullable) NSView *actionSender;
|
||||
- (instancetype)initWithAPIManager:(id<PROAPIAccessing>)manager;
|
||||
- (void)refresh;
|
||||
- (void)documentDidChange;
|
||||
- (void)beginInteraction;
|
||||
- (void)endInteraction;
|
||||
- (BOOL)setValue:(double)value forParameter:(UInt32)identifier;
|
||||
- (BOOL)toggleEnabledForParameter:(UInt32)identifier;
|
||||
- (BOOL)resetParameter:(UInt32)identifier;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,270 @@
|
||||
#import "PGInspectorState.h"
|
||||
#import <math.h>
|
||||
#import <stddef.h>
|
||||
#import <os/log.h>
|
||||
|
||||
NSArray<NSDictionary *> *PGInspectorControls(void) {
|
||||
static NSArray *controls; static dispatch_once_t once;
|
||||
dispatch_once(&once, ^{
|
||||
PGBeautySettings defaults=PGBeautyDefaults();
|
||||
NSMutableArray *items=[NSMutableArray new];
|
||||
#define ITEM(ID,NAME,FIELD,SECTION,KIND,FORMAT,MIN,MAX,BYPASS,STORAGE) \
|
||||
[items addObject:@{@"id":@(ID),@"name":@NAME,@"section":@SECTION,@"kind":@KIND,@"format":@FORMAT,@"min":@(MIN),@"max":@(MAX),@"default":@(defaults.FIELD),@"bypass":@(BYPASS),@"offset":@(offsetof(PGBeautySettings,FIELD)),@"storage":@STORAGE}]
|
||||
ITEM(PGSmoothingEnabled,"磨皮",featureProtection,"smooth","master","integer",0,1,NO,"master");
|
||||
ITEM(PGMidtone,"局部磨皮",midtoneDetail,"smooth","slider","percent",0,1,YES,"float");
|
||||
ITEM(PGHighPass,"全局磨皮",highPassDetail,"smooth","slider","percent",0,1,YES,"float");
|
||||
ITEM(PGFeatureProtect,"五官保护",featureProtection,"smooth","toggle","integer",0,1,NO,"uint");
|
||||
ITEM(PGColorEnabled,"美白",featureProtection,"color","master","integer",0,1,NO,"master");
|
||||
ITEM(PGTonePreset,"肤色",skinTonePreset,"color","preset","integer",0,6,NO,"uint");
|
||||
NSMutableDictionary *preset=[items.lastObject mutableCopy];
|
||||
preset[@"options"]=@[@"原色",@"自然白",@"冷白",@"暖白",@"红润白",@"小麦",@"古铜"];items[items.count-1]=preset;
|
||||
ITEM(PGBrightness,"肤色亮度",skinBrightness,"color","slider","percent",0,1,YES,"float");
|
||||
ITEM(PGTeeth,"白牙",teethWhitening,"color","slider","percent",0,1,YES,"float");
|
||||
ITEM(PGHue,"皮肤色相",skinHue,"color","slider","degrees",-30,30,YES,"float");
|
||||
ITEM(PGShapeEnabled,"瘦脸",featureProtection,"shape","master","integer",0,1,NO,"master");
|
||||
ITEM(PGTrackBoxes,"人脸框预览",showTrackBox,"shape","toggle","integer",0,1,NO,"uint");
|
||||
ITEM(PGFastMulti,"多人模式",fastMultiFace,"shape","toggle","integer",0,1,NO,"uint");
|
||||
ITEM(PGJumpGuard,"防跳脸范围",faceJumpGuardRange,"shape","slider","percent",1,4,NO,"float");
|
||||
ITEM(PGThin,"瘦脸",faceThin,"shape","slider","signed",0,1,YES,"float");
|
||||
ITEM(PGBigEye,"大眼",faceBigEye,"shape","slider","signed",0,1,YES,"float");
|
||||
ITEM(PGShort,"短脸",faceShort,"shape","slider","signed",-1,1,YES,"float");
|
||||
ITEM(PGNarrow,"窄脸",faceNarrow,"shape","slider","signed",-1,1,YES,"float");
|
||||
ITEM(PGChin,"下巴",faceChin,"shape","slider","signed",-1,1,YES,"float");
|
||||
ITEM(PGVFace,"V脸",faceV,"shape","slider","signed",0,1,YES,"float");
|
||||
ITEM(PGJaw,"下颌骨",faceJaw,"shape","slider","signed",0,1,YES,"float");
|
||||
ITEM(PGCheekbone,"颧骨",faceCheekbone,"shape","slider","signed",0,1,YES,"float");
|
||||
ITEM(PGSmallHead,"小头",faceSmallHead,"shape","slider","signed",0,1,YES,"float");
|
||||
ITEM(PGSmallHeadRange,"小头范围",faceSmallHeadRange,"shape","slider","percent",0,1,NO,"float");
|
||||
ITEM(PGAmount,"磨皮强度",amount,"more","slider","percent",0,1,YES,"float");
|
||||
ITEM(PGRadius,"磨皮半径",radius,"more","slider","radius",1,30,NO,"float");
|
||||
ITEM(PGSaturation,"肤色饱和度",skinSaturation,"more","slider","percent",-100,100,YES,"float");
|
||||
ITEM(PGEvenTone,"均匀肤色",skinEvenTone,"more","slider","percent",0,1,YES,"float");
|
||||
ITEM(PGEyeBright,"眼睛提亮",eyeBrightening,"more","slider","percent",0,1,YES,"float");
|
||||
ITEM(PGSharpness,"锐化",sharpness,"more","slider","percent",0,1,YES,"float");
|
||||
ITEM(PGMix,"整体混合",mix,"more","slider","percent",0,1,NO,"float");
|
||||
#undef ITEM
|
||||
controls=[items copy];
|
||||
}); return controls;
|
||||
}
|
||||
NSDictionary *PGInspectorDescriptor(UInt32 identifier) {
|
||||
for(NSDictionary *d in PGInspectorControls()) if([d[@"id"] unsignedIntValue]==identifier) return d;
|
||||
return nil;
|
||||
}
|
||||
static double PGControlValue(id raw,NSDictionary *d) {
|
||||
double value=[raw isKindOfClass:NSNumber.class]?[raw doubleValue]:[d[@"default"] doubleValue];
|
||||
if(!isfinite(value)) value=[d[@"default"] doubleValue];
|
||||
value=fmin([d[@"max"] doubleValue],fmax([d[@"min"] doubleValue],value));
|
||||
if(![d[@"storage"] isEqual:@"float"]) value=round(value);
|
||||
return value;
|
||||
}
|
||||
@implementation PGInspectorState
|
||||
+ (BOOL)supportsSecureCoding {return YES;}
|
||||
+ (instancetype)stateWithValues:(NSDictionary *)values disabledParameters:(NSSet *)disabled hasEdits:(BOOL)edited {
|
||||
PGInspectorState *state=[self new]; NSMutableDictionary *safe=[NSMutableDictionary new];NSMutableSet *bypassed=[NSMutableSet new];
|
||||
for(NSDictionary *d in PGInspectorControls()) {
|
||||
NSNumber *key=d[@"id"];safe[key]=@(PGControlValue(values[key],d));
|
||||
if([d[@"bypass"] boolValue] && [disabled containsObject:key]) [bypassed addObject:key];
|
||||
}
|
||||
state->_values=[safe copy];state->_disabledParameters=[bypassed copy];state->_hasEdits=edited;return state;
|
||||
}
|
||||
- (instancetype)init {if((self=[super init])) {_values=@{};_disabledParameters=[NSSet set];}return self;}
|
||||
- (instancetype)initWithCoder:(NSCoder *)coder {
|
||||
NSInteger version=[coder decodeIntegerForKey:@"schema"];
|
||||
if(version!=1) return nil;
|
||||
NSSet *classes=[NSSet setWithObjects:NSDictionary.class,NSNumber.class,nil];
|
||||
NSDictionary *values=[coder decodeObjectOfClasses:classes forKey:@"values"];
|
||||
NSSet *disabled=[coder decodeObjectOfClasses:[NSSet setWithObjects:NSSet.class,NSNumber.class,nil] forKey:@"disabled"];
|
||||
return [PGInspectorState stateWithValues:values?:@{} disabledParameters:disabled?:[NSSet set] hasEdits:[coder decodeBoolForKey:@"edited"]];
|
||||
}
|
||||
- (void)encodeWithCoder:(NSCoder *)coder {
|
||||
[coder encodeInteger:1 forKey:@"schema"];[coder encodeObject:_values forKey:@"values"];
|
||||
[coder encodeObject:_disabledParameters forKey:@"disabled"];[coder encodeBool:_hasEdits forKey:@"edited"];
|
||||
}
|
||||
- (id)copyWithZone:(NSZone *)zone {return [PGInspectorState stateWithValues:_values disabledParameters:_disabledParameters hasEdits:_hasEdits];}
|
||||
- (double)valueForParameter:(UInt32)identifier {NSDictionary *d=PGInspectorDescriptor(identifier);return d?PGControlValue(_values[@(identifier)],d):0;}
|
||||
- (BOOL)isParameterEnabled:(UInt32)identifier {return ![_disabledParameters containsObject:@(identifier)];}
|
||||
- (BOOL)isEqual:(id)object {
|
||||
if(object==self)return YES;
|
||||
if(![object isKindOfClass:PGInspectorState.class])return NO;
|
||||
PGInspectorState *other=object;return _hasEdits==other.hasEdits && [_values isEqual:other.values] && [_disabledParameters isEqual:other.disabledParameters];
|
||||
}
|
||||
- (NSUInteger)hash {return _values.hash ^ _disabledParameters.hash ^ _hasEdits;}
|
||||
- (NSObject<NSSecureCoding,NSCopying> *)interpolateBetween:(NSObject<NSSecureCoding,NSCopying> *)rightValue withWeight:(float)weight {
|
||||
if(![rightValue isKindOfClass:PGInspectorState.class])return [self copy];
|
||||
PGInspectorState *right=(PGInspectorState *)rightValue;double t=isfinite(weight)?fmin(1,fmax(0,weight)):0;
|
||||
if(t==0)return [self copy];if(t==1)return [right copy];
|
||||
if(!_hasEdits || !right.hasEdits)return [self copy];
|
||||
NSMutableDictionary *values=[NSMutableDictionary new];
|
||||
for(NSDictionary *d in PGInspectorControls()) {
|
||||
UInt32 key=[d[@"id"] unsignedIntValue];double a=[self valueForParameter:key],b=[right valueForParameter:key];
|
||||
// Menus, switches and enable masks hold the preceding key until the
|
||||
// next key. Numeric slider values interpolate within their valid range.
|
||||
values[@(key)]=[d[@"storage"] isEqual:@"float"]?@(a+(b-a)*t):@(a);
|
||||
}
|
||||
return [PGInspectorState stateWithValues:values disabledParameters:_disabledParameters hasEdits:_hasEdits||right.hasEdits];
|
||||
}
|
||||
@end
|
||||
|
||||
PGBeautySettings PGSettingsWithInspectorState(PGBeautySettings s,PGInspectorState *state) {
|
||||
if(!state.hasEdits)return s;
|
||||
for(NSDictionary *d in PGInspectorControls()) {
|
||||
NSString *storage=d[@"storage"];if([storage isEqual:@"master"])continue;
|
||||
UInt32 key=[d[@"id"] unsignedIntValue];double value=[state valueForParameter:key];
|
||||
if(![state isParameterEnabled:key]) value=0;
|
||||
size_t offset=[d[@"offset"] unsignedIntegerValue];
|
||||
if([storage isEqual:@"float"]) *(float *)((uint8_t *)&s+offset)=value;
|
||||
else *(uint32_t *)((uint8_t *)&s+offset)=(uint32_t)value;
|
||||
}
|
||||
if(![state valueForParameter:PGSmoothingEnabled])s.amount=s.midtoneDetail=s.highPassDetail=0;
|
||||
if(![state valueForParameter:PGColorEnabled]) {s.skinBrightness=s.skinHue=s.skinSaturation=s.skinEvenTone=s.teethWhitening=s.eyeBrightening=0;s.skinTonePreset=0;}
|
||||
if(![state valueForParameter:PGShapeEnabled]) {s.faceThin=s.faceBigEye=s.faceShort=s.faceNarrow=s.faceChin=s.faceV=s.faceJaw=s.faceCheekbone=s.faceSmallHead=0;s.showTrackBox=0;}
|
||||
return s;
|
||||
}
|
||||
|
||||
@interface PGInspectorController ()
|
||||
@property (nonatomic,strong,readwrite) PGInspectorState *state;
|
||||
@property (nonatomic,readwrite) BOOL available;
|
||||
@property (nonatomic,copy,readwrite) NSString *statusMessage;
|
||||
@property (nonatomic,strong) id<PROAPIAccessing> manager;
|
||||
@property (nonatomic,strong) id<FxCustomParameterActionAPI_v4> action;
|
||||
@property (nonatomic) BOOL interacting;
|
||||
@property (nonatomic) NSTimeInterval pendingUntil;
|
||||
@property (nonatomic) CMTime pendingTime;
|
||||
@property (nonatomic, strong) id activeSender;
|
||||
@property (nonatomic) BOOL reportedConnection;
|
||||
@property (nonatomic) BOOL reportedMissingAction;
|
||||
@end
|
||||
@implementation PGInspectorController
|
||||
- (instancetype)initWithAPIManager:(id<PROAPIAccessing>)manager {
|
||||
if((self=[super init])){_manager=manager;_state=[PGInspectorState stateWithValues:@{} disabledParameters:[NSSet set] hasEdits:NO];_statusMessage=@"";}
|
||||
return self;
|
||||
}
|
||||
- (BOOL)start {
|
||||
if(_interacting)return YES;
|
||||
_action=[_manager apiForProtocol:@protocol(FxCustomParameterActionAPI_v4)];
|
||||
if(!_action){
|
||||
_available=NO;_statusMessage=@"等待宿主连接";
|
||||
if(!_reportedMissingAction){os_log(OS_LOG_DEFAULT,"PGInspector action API unavailable manager=%{public}d viewSender=%{public}d onMain=%{public}d",_manager!=nil,_actionSender!=nil,NSThread.isMainThread);_reportedMissingAction=YES;}
|
||||
return NO;
|
||||
}
|
||||
// A native host can expose parameter APIs only after the custom view's
|
||||
// action has established its document context. Never gate startAction on
|
||||
// those APIs being available before entering the action.
|
||||
BOOL readBefore=NO,writeBefore=NO;
|
||||
if(!_reportedConnection){
|
||||
readBefore=[_manager apiForProtocol:@protocol(FxParameterRetrievalAPI_v6)]!=nil;
|
||||
writeBefore=[_manager apiForProtocol:@protocol(FxParameterSettingAPI_v5)]!=nil;
|
||||
}
|
||||
_activeSender=_actionSender ?: self;
|
||||
[_action startAction:_activeSender];_interacting=YES;
|
||||
@try {
|
||||
id retrieval=[_manager apiForProtocol:@protocol(FxParameterRetrievalAPI_v6)];
|
||||
id setting=[_manager apiForProtocol:@protocol(FxParameterSettingAPI_v5)];
|
||||
_available=retrieval && setting;
|
||||
if(!_reportedConnection){
|
||||
os_log(OS_LOG_DEFAULT,"PGInspector connection readBefore=%{public}d writeBefore=%{public}d readInAction=%{public}d writeInAction=%{public}d viewSender=%{public}d onMain=%{public}d",readBefore,writeBefore,retrieval!=nil,setting!=nil,[_activeSender isKindOfClass:NSView.class],NSThread.isMainThread);
|
||||
_reportedConnection=YES;
|
||||
}
|
||||
if(!_available){_statusMessage=@"等待宿主连接";[self finish];return NO;}
|
||||
} @catch(NSException *exception) {[self finish];@throw exception;}
|
||||
return YES;
|
||||
}
|
||||
- (void)finish {if(_interacting){_interacting=NO;[_action endAction:_activeSender];_action=nil;_activeSender=nil;}}
|
||||
- (void)readAtTime:(CMTime)time {
|
||||
if(_pendingUntil && CMTimeCompare(time,_pendingTime)!=0)_pendingUntil=0;
|
||||
id<FxParameterRetrievalAPI_v6> api=[_manager apiForProtocol:@protocol(FxParameterRetrievalAPI_v6)];
|
||||
NSObject *object=nil;[api getCustomParameterValue:&object fromParameter:PGInspectorPanel atTime:time];
|
||||
if([object isKindOfClass:PGInspectorState.class] && [(PGInspectorState *)object hasEdits]) {
|
||||
PGInspectorState *host=(PGInspectorState *)object;
|
||||
if([host isEqual:_state])_pendingUntil=0;
|
||||
if(!_pendingUntil || NSDate.timeIntervalSinceReferenceDate>=_pendingUntil)_state=[host copy];
|
||||
return;
|
||||
}
|
||||
if(_pendingUntil && NSDate.timeIntervalSinceReferenceDate<_pendingUntil)return;
|
||||
NSMutableDictionary *values=[NSMutableDictionary new];
|
||||
for(NSDictionary *d in PGInspectorControls()) {
|
||||
UInt32 key=[d[@"id"] unsignedIntValue];double value=[d[@"default"] doubleValue];
|
||||
if([d[@"kind"] isEqual:@"preset"]) {int menu=(int)value;if([api getIntValue:&menu fromParameter:key atTime:time])value=menu;}
|
||||
else if([d[@"kind"] isEqual:@"toggle"] || [d[@"kind"] isEqual:@"master"]) {BOOL on=value!=0;if([api getBoolValue:&on fromParameter:key atTime:time])value=on;}
|
||||
else [api getFloatValue:&value fromParameter:key atTime:time];
|
||||
values[@(key)]=@(value);
|
||||
}
|
||||
BOOL legacyDetail=YES;[api getBoolValue:&legacyDetail fromParameter:PGDetailEnabled atTime:time];
|
||||
NSMutableSet *disabled=legacyDetail?[NSMutableSet set]:[NSMutableSet setWithObjects:@(PGHighPass),@(PGSharpness),nil];
|
||||
// Earlier versions put high-pass smoothing under a different group. Keep
|
||||
// its effective state when importing a document whose local-skin group was
|
||||
// off, rather than turning a still-active global effect off on first edit.
|
||||
if(![values[@(PGSmoothingEnabled)] boolValue] && legacyDetail && [values[@(PGHighPass)] doubleValue]>0) {
|
||||
values[@(PGSmoothingEnabled)]=@YES;
|
||||
[disabled addObject:@(PGMidtone)];[disabled addObject:@(PGAmount)];
|
||||
}
|
||||
_state=[PGInspectorState stateWithValues:values disabledParameters:disabled hasEdits:NO];
|
||||
}
|
||||
- (void)refresh {
|
||||
if(_interacting)return;
|
||||
if(![self start])return;
|
||||
@try {[self readAtTime:[_action currentTime]];_statusMessage=@"";} @finally {[self finish];}
|
||||
}
|
||||
- (void)documentDidChange {if(_interacting)return;_pendingUntil=0;[self refresh];}
|
||||
- (void)beginInteraction {
|
||||
if(_interacting)return;
|
||||
if([self start]) {
|
||||
// A new gesture starts from the authoritative document, including an
|
||||
// immediate undo. The short pending hold only prevents display flicker;
|
||||
// it must never resurrect an undone value in a subsequent edit.
|
||||
_pendingUntil=0;[self readAtTime:[_action currentTime]];
|
||||
}
|
||||
}
|
||||
- (void)endInteraction {[self finish];}
|
||||
- (BOOL)commit:(PGInspectorState *)next {
|
||||
if([next isEqual:_state] && _state.hasEdits)return YES;
|
||||
id<FxParameterSettingAPI_v5> api=[_manager apiForProtocol:@protocol(FxParameterSettingAPI_v5)];
|
||||
CMTime time=[_action currentTime];
|
||||
// The published object is authoritative. Only mirror after its write was
|
||||
// accepted; a rejected write must never leave half-applied hidden values.
|
||||
if(![api setCustomParameterValue:next toParameter:PGInspectorPanel atTime:time]){_statusMessage=@"参数未能保存,请重试";return NO;}
|
||||
for(NSDictionary *d in PGInspectorControls()) {
|
||||
UInt32 key=[d[@"id"] unsignedIntValue];double value=[next valueForParameter:key];
|
||||
if(![next isParameterEnabled:key])value=0;
|
||||
double previous=[_state isParameterEnabled:key]?[_state valueForParameter:key]:0;
|
||||
if(previous==value)continue;
|
||||
if([d[@"kind"] isEqual:@"preset"]) [api setIntValue:(int)value toParameter:key atTime:time];
|
||||
else if([d[@"kind"] isEqual:@"toggle"] || [d[@"kind"] isEqual:@"master"]) [api setBoolValue:value!=0 toParameter:key atTime:time];
|
||||
else [api setFloatValue:value toParameter:key atTime:time];
|
||||
}
|
||||
_state=next;_pendingUntil=NSDate.timeIntervalSinceReferenceDate+.35;_pendingTime=time;_statusMessage=@"";return YES;
|
||||
}
|
||||
- (BOOL)setValue:(double)value forParameter:(UInt32)identifier {
|
||||
NSDictionary *d=PGInspectorDescriptor(identifier);if(!d || !isfinite(value))return NO;
|
||||
BOOL owned=!_interacting;if(owned)[self beginInteraction];if(!_interacting)return NO;
|
||||
BOOL ok=NO;@try {
|
||||
NSMutableDictionary *values=[_state.values mutableCopy];values[@(identifier)]=@(PGControlValue(@(value),d));
|
||||
ok=[self commit:[PGInspectorState stateWithValues:values disabledParameters:_state.disabledParameters hasEdits:YES]];
|
||||
} @finally {if(owned)[self finish];}return ok;
|
||||
}
|
||||
- (BOOL)toggleEnabledForParameter:(UInt32)identifier {
|
||||
NSDictionary *d=PGInspectorDescriptor(identifier);if(!d)return NO;
|
||||
if([d[@"kind"] isEqual:@"master"] || [d[@"kind"] isEqual:@"toggle"]) {
|
||||
BOOL owned=!_interacting;if(owned)[self beginInteraction];if(!_interacting)return NO;
|
||||
BOOL ok=NO;@try {ok=[self setValue:![_state valueForParameter:identifier] forParameter:identifier];}
|
||||
@finally {if(owned)[self finish];}return ok;
|
||||
}
|
||||
if(![d[@"bypass"] boolValue])return NO;
|
||||
BOOL owned=!_interacting;if(owned)[self beginInteraction];if(!_interacting)return NO;
|
||||
BOOL ok=NO;@try {
|
||||
NSMutableSet *disabled=[_state.disabledParameters mutableCopy];NSNumber *key=@(identifier);
|
||||
if([disabled containsObject:key])[disabled removeObject:key];else [disabled addObject:key];
|
||||
ok=[self commit:[PGInspectorState stateWithValues:_state.values disabledParameters:disabled hasEdits:YES]];
|
||||
} @finally {if(owned)[self finish];}return ok;
|
||||
}
|
||||
- (BOOL)resetParameter:(UInt32)identifier {
|
||||
NSDictionary *d=PGInspectorDescriptor(identifier);if(!d)return NO;
|
||||
BOOL owned=!_interacting;if(owned)[self beginInteraction];if(!_interacting)return NO;
|
||||
BOOL ok=NO;@try {
|
||||
NSMutableDictionary *values=[_state.values mutableCopy];values[@(identifier)]=d[@"default"];
|
||||
NSMutableSet *disabled=[_state.disabledParameters mutableCopy];[disabled removeObject:@(identifier)];
|
||||
ok=[self commit:[PGInspectorState stateWithValues:values disabledParameters:disabled hasEdits:YES]];
|
||||
} @finally {if(owned)[self finish];}return ok;
|
||||
}
|
||||
- (void)dealloc {if(_interacting)[_action endAction:_activeSender];}
|
||||
@end
|
||||
@@ -0,0 +1,16 @@
|
||||
#import <AppKit/AppKit.h>
|
||||
#import "PGInspectorState.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// Native inspector controls. Host access belongs exclusively to the controller.
|
||||
@interface PGInspectorView : NSView
|
||||
@property (nonatomic, strong, readonly) PGInspectorController *controller;
|
||||
- (instancetype)initWithAPIManager:(id<PROAPIAccessing>)manager;
|
||||
/// Also permits a deterministic controller in an offscreen preview or test.
|
||||
- (instancetype)initWithController:(PGInspectorController *)controller;
|
||||
- (void)refreshFromHost;
|
||||
- (void)hostParameterChanged;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,367 @@
|
||||
#import "PGInspectorView.h"
|
||||
#include <math.h>
|
||||
|
||||
static NSColor *PGUIColor(CGFloat r, CGFloat g, CGFloat b, CGFloat a) {
|
||||
return [NSColor colorWithSRGBRed:r green:g blue:b alpha:a];
|
||||
}
|
||||
static NSColor *PGUIText(void) { return PGUIColor(.88,.91,.95,1); }
|
||||
static NSColor *PGUIMuted(void) { return PGUIColor(.60,.65,.73,1); }
|
||||
static NSTextField *PGUILabel(NSString *text, CGFloat size, NSFontWeight weight) {
|
||||
NSTextField *label=[NSTextField labelWithString:text ?: @""];
|
||||
label.font=[NSFont systemFontOfSize:size weight:weight]; label.textColor=PGUIText();
|
||||
label.lineBreakMode=NSLineBreakByTruncatingTail; return label;
|
||||
}
|
||||
static CGFloat PGValueScale(NSDictionary *descriptor) {
|
||||
return [descriptor[@"format"] isEqual:@"percent"] && [descriptor[@"max"] doubleValue]<=10 ? 100 : 1;
|
||||
}
|
||||
static NSString *PGFormattedValue(NSDictionary *descriptor, double value) {
|
||||
NSString *format=descriptor[@"format"];
|
||||
if ([format isEqual:@"percent"]) return [NSString stringWithFormat:@"%.0f%%",value*PGValueScale(descriptor)];
|
||||
if ([format isEqual:@"degrees"]) return [NSString stringWithFormat:@"%+.0f°",value];
|
||||
if ([format isEqual:@"signed"]) return [NSString stringWithFormat:@"%.2f",value];
|
||||
if ([format isEqual:@"integer"]) return [NSString stringWithFormat:@"%.0f",value];
|
||||
return [NSString stringWithFormat:@"%.1f",value];
|
||||
}
|
||||
|
||||
@interface PGInspectorGlyphButton : NSButton
|
||||
@property (nonatomic, copy) NSString *glyph;
|
||||
@property (nonatomic, strong) NSColor *accent;
|
||||
@property (nonatomic, strong) NSColor *swatch;
|
||||
@end
|
||||
@implementation PGInspectorGlyphButton
|
||||
- (BOOL)isFlipped { return YES; }
|
||||
- (void)drawRect:(NSRect)dirtyRect {
|
||||
(void)dirtyRect; NSRect b=NSInsetRect(self.bounds,1,1);
|
||||
CGFloat opacity=self.enabled?1:.35;
|
||||
NSColor *accent=[self.accent colorWithAlphaComponent:opacity];
|
||||
BOOL on=self.state==NSControlStateValueOn;
|
||||
if ([self.glyph isEqual:@"swatch"]) {
|
||||
NSRect circle=NSInsetRect(b,3,3);
|
||||
[[(self.swatch ?: PGUIColor(.12,.19,.23,1)) colorWithAlphaComponent:opacity] setFill]; [[NSBezierPath bezierPathWithOvalInRect:circle] fill];
|
||||
[[PGUIText() colorWithAlphaComponent:.25*opacity] setStroke];
|
||||
NSBezierPath *ring=[NSBezierPath bezierPathWithOvalInRect:circle];ring.lineWidth=1;[ring stroke];
|
||||
if (!self.swatch) {
|
||||
[PGUIMuted() setStroke];NSBezierPath *slash=[NSBezierPath bezierPath];
|
||||
[slash moveToPoint:NSMakePoint(NSMinX(circle)+4,NSMinY(circle)+4)];[slash lineToPoint:NSMakePoint(NSMaxX(circle)-4,NSMaxY(circle)-4)];slash.lineWidth=1.5;[slash stroke];
|
||||
}
|
||||
if(on) { [accent setStroke];NSBezierPath *selection=[NSBezierPath bezierPathWithOvalInRect:NSInsetRect(b,.5,.5)];selection.lineWidth=1.5;[selection stroke]; }
|
||||
} else if ([self.glyph isEqual:@"switch"]) {
|
||||
NSRect pill=NSInsetRect(b,1,3);
|
||||
[(on?accent:PGUIColor(.20,.25,.34,opacity)) setFill];[[NSBezierPath bezierPathWithRoundedRect:pill xRadius:NSHeight(pill)/2 yRadius:NSHeight(pill)/2]fill];
|
||||
CGFloat diameter=NSHeight(pill)-4;CGFloat x=on?NSMaxX(pill)-diameter-2:NSMinX(pill)+2;
|
||||
[[NSColor colorWithWhite:.98 alpha:opacity]setFill];[[NSBezierPath bezierPathWithOvalInRect:NSMakeRect(x,NSMinY(pill)+2,diameter,diameter)]fill];
|
||||
} else {
|
||||
NSBezierPath *back=[NSBezierPath bezierPathWithRoundedRect:b xRadius:7 yRadius:7];
|
||||
[[self.accent colorWithAlphaComponent:([self.glyph isEqual:@"eye"]?.055:.13)*opacity]setFill];[back fill];
|
||||
if ([self.glyph isEqual:@"eye"]) {
|
||||
[[self.accent colorWithAlphaComponent:.35*opacity]setStroke];back.lineWidth=.7;[back stroke];
|
||||
NSPoint c=NSMakePoint(NSMidX(b),NSMidY(b));
|
||||
NSBezierPath *eye=[NSBezierPath bezierPath];[eye moveToPoint:NSMakePoint(c.x-6,c.y)];
|
||||
[eye curveToPoint:NSMakePoint(c.x+6,c.y) controlPoint1:NSMakePoint(c.x-3,c.y-5.5) controlPoint2:NSMakePoint(c.x+3,c.y-5.5)];
|
||||
[eye curveToPoint:NSMakePoint(c.x-6,c.y) controlPoint1:NSMakePoint(c.x+3,c.y+5.5) controlPoint2:NSMakePoint(c.x-3,c.y+5.5)];eye.lineWidth=1.3;[accent setStroke];[eye stroke];
|
||||
if(on){[accent setFill];[[NSBezierPath bezierPathWithOvalInRect:NSMakeRect(c.x-1.8,c.y-1.8,3.6,3.6)]fill];}
|
||||
else{NSBezierPath *slash=[NSBezierPath bezierPath];[slash moveToPoint:NSMakePoint(c.x-6,c.y+6)];[slash lineToPoint:NSMakePoint(c.x+6,c.y-6)];slash.lineWidth=1.2;[slash stroke];}
|
||||
} else {
|
||||
NSString *symbol=[self.glyph isEqual:@"disclosure"]?(on?@"⌄":@"›"):@"↻";
|
||||
NSFont *font=[NSFont systemFontOfSize:[self.glyph isEqual:@"disclosure"]?19:18 weight:NSFontWeightSemibold];
|
||||
NSDictionary *attributes=@{NSFontAttributeName:font,NSForegroundColorAttributeName:[self.accent colorWithAlphaComponent:(on?1:.58)*opacity]};
|
||||
NSSize size=[symbol sizeWithAttributes:attributes];[symbol drawAtPoint:NSMakePoint(NSMidX(b)-size.width/2,NSMidY(b)-size.height/2)withAttributes:attributes];
|
||||
}
|
||||
}
|
||||
if(self.window.firstResponder==self){[[self.accent colorWithAlphaComponent:.8]setStroke];NSBezierPath *focus=[NSBezierPath bezierPathWithRoundedRect:NSInsetRect(self.bounds,.5,.5) xRadius:7 yRadius:7];focus.lineWidth=1;[focus stroke];}
|
||||
}
|
||||
- (void)setState:(NSControlStateValue)state { [super setState:state];self.needsDisplay=YES; }
|
||||
- (void)setEnabled:(BOOL)enabled { [super setEnabled:enabled];self.needsDisplay=YES; }
|
||||
@end
|
||||
static PGInspectorGlyphButton *PGUIButton(NSString *glyph, NSColor *accent, id target, SEL action) {
|
||||
PGInspectorGlyphButton *button=[[PGInspectorGlyphButton alloc]initWithFrame:NSZeroRect];
|
||||
button.glyph=glyph;button.accent=accent;button.bordered=NO;button.title=@"";
|
||||
[button setButtonType:([glyph isEqual:@"eye"]||[glyph isEqual:@"switch"]||[glyph isEqual:@"disclosure"]||[glyph isEqual:@"swatch"])?NSButtonTypePushOnPushOff:NSButtonTypeMomentaryPushIn];
|
||||
button.target=target;button.action=action;button.focusRingType=NSFocusRingTypeExterior;
|
||||
return button;
|
||||
}
|
||||
|
||||
@interface PGInspectorSliderCell : NSSliderCell
|
||||
@property (nonatomic, strong) NSColor *accent;
|
||||
@end
|
||||
@implementation PGInspectorSliderCell
|
||||
- (void)drawBarInside:(NSRect)rect flipped:(BOOL)flipped {
|
||||
(void)flipped;NSRect bar=NSMakeRect(NSMinX(rect),NSMidY(rect)-2,NSWidth(rect),4);
|
||||
[PGUIColor(.08,.13,.20,self.enabled?1:.5)setFill];NSBezierPath *track=[NSBezierPath bezierPathWithRoundedRect:bar xRadius:2 yRadius:2];[track fill];
|
||||
[[self.accent colorWithAlphaComponent:.18]setStroke];track.lineWidth=.7;[track stroke];
|
||||
double range=self.maxValue-self.minValue;if(range<=0)return;
|
||||
CGFloat from=self.minValue<0?(CGFloat)((0-self.minValue)/range):0;
|
||||
CGFloat to=(CGFloat)((self.doubleValue-self.minValue)/range);
|
||||
NSRect fill=NSMakeRect(NSMinX(bar)+MIN(from,to)*NSWidth(bar),NSMinY(bar),fabs(to-from)*NSWidth(bar),NSHeight(bar));
|
||||
[[self.accent colorWithAlphaComponent:self.enabled?.85:.23]setFill];[[NSBezierPath bezierPathWithRoundedRect:fill xRadius:2 yRadius:2]fill];
|
||||
}
|
||||
- (void)drawKnob:(NSRect)knobRect {
|
||||
CGFloat d=12;NSRect r=NSMakeRect(NSMidX(knobRect)-d/2,NSMidY(knobRect)-d/2,d,d);
|
||||
[[self.accent colorWithAlphaComponent:self.enabled?.22:.08]setFill];[[NSBezierPath bezierPathWithOvalInRect:NSInsetRect(r,-2,-2)]fill];
|
||||
[[NSColor colorWithWhite:.97 alpha:self.enabled?1:.5]setFill];NSBezierPath *knob=[NSBezierPath bezierPathWithOvalInRect:r];[knob fill];
|
||||
[[self.accent colorWithAlphaComponent:self.enabled?.85:.25]setStroke];knob.lineWidth=1;[knob stroke];
|
||||
}
|
||||
- (CGFloat)knobThickness { return 14; }
|
||||
@end
|
||||
@interface PGInspectorSlider : NSSlider
|
||||
@property (nonatomic, copy) void (^interactionBegan)(void);
|
||||
@property (nonatomic, copy) void (^interactionEnded)(void);
|
||||
@end
|
||||
@implementation PGInspectorSlider
|
||||
- (void)mouseDown:(NSEvent *)event {
|
||||
if(!self.enabled)return;if(self.interactionBegan)self.interactionBegan();
|
||||
@try{[super mouseDown:event];}@finally{if(self.interactionEnded)self.interactionEnded();}
|
||||
}
|
||||
- (void)keyDown:(NSEvent *)event {
|
||||
if(self.interactionBegan)self.interactionBegan();
|
||||
@try{[super keyDown:event];}@finally{if(self.interactionEnded)self.interactionEnded();}
|
||||
}
|
||||
@end
|
||||
|
||||
@interface PGInspectorNumberCell : NSTextFieldCell
|
||||
@end
|
||||
@implementation PGInspectorNumberCell
|
||||
- (NSRect)drawingRectForBounds:(NSRect)bounds {
|
||||
NSRect rect=[super drawingRectForBounds:bounds];CGFloat height=[self cellSizeForBounds:bounds].height;
|
||||
if(NSHeight(rect)>height){rect.origin.y+=(NSHeight(rect)-height)*.5;rect.size.height=height;}return rect;
|
||||
}
|
||||
@end
|
||||
@interface PGInspectorNumberField : NSTextField
|
||||
@end
|
||||
@implementation PGInspectorNumberField
|
||||
+ (Class)cellClass { return PGInspectorNumberCell.class; }
|
||||
@end
|
||||
|
||||
@class PGInspectorRow;
|
||||
@interface PGInspectorCard : NSView
|
||||
@property (nonatomic, strong) NSColor *accent;
|
||||
@property (nonatomic, copy) NSString *heading;
|
||||
@property (nonatomic, strong) NSTextField *titleLabel;
|
||||
@property (nonatomic, strong) PGInspectorGlyphButton *master;
|
||||
@property (nonatomic, copy) NSArray<PGInspectorRow *> *rows;
|
||||
@property (nonatomic) BOOL expanded;
|
||||
- (CGFloat)heightForWidth:(CGFloat)width;
|
||||
@end
|
||||
@interface PGInspectorView ()
|
||||
@property (nonatomic, strong, readwrite) PGInspectorController *controller;
|
||||
- (void)reloadControls;
|
||||
- (void)beginControlInteraction;
|
||||
- (void)endControlInteraction;
|
||||
- (void)writeValue:(double)value parameter:(UInt32)identifier;
|
||||
@end
|
||||
@interface PGInspectorRow : NSView <NSTextFieldDelegate>
|
||||
@property (nonatomic, weak) PGInspectorView *inspector;
|
||||
@property (nonatomic, copy) NSDictionary *descriptor;
|
||||
@property (nonatomic, strong) NSColor *accent;
|
||||
@property (nonatomic, strong) NSTextField *label;
|
||||
@property (nonatomic, strong) NSTextField *valueField;
|
||||
@property (nonatomic) BOOL numberHasUnsubmittedEdit;
|
||||
@property (nonatomic, strong) NSTextField *hint;
|
||||
@property (nonatomic, strong) PGInspectorSlider *slider;
|
||||
@property (nonatomic, strong) PGInspectorGlyphButton *eye;
|
||||
@property (nonatomic, strong) PGInspectorGlyphButton *reset;
|
||||
@property (nonatomic, strong) PGInspectorGlyphButton *toggle;
|
||||
@property (nonatomic, copy) NSArray<PGInspectorGlyphButton *> *swatches;
|
||||
- (instancetype)initWithDescriptor:(NSDictionary *)descriptor accent:(NSColor *)accent inspector:(PGInspectorView *)inspector;
|
||||
- (CGFloat)heightForWidth:(CGFloat)width;
|
||||
- (void)reloadState:(PGInspectorState *)state master:(BOOL)master available:(BOOL)available;
|
||||
@end
|
||||
@implementation PGInspectorRow
|
||||
- (BOOL)isFlipped { return YES; }
|
||||
- (instancetype)initWithDescriptor:(NSDictionary *)descriptor accent:(NSColor *)accent inspector:(PGInspectorView *)inspector {
|
||||
if((self=[super initWithFrame:NSZeroRect])) {
|
||||
_descriptor=[descriptor copy];_accent=accent;_inspector=inspector;
|
||||
self.identifier=[NSString stringWithFormat:@"pg.parameter.%@",descriptor[@"id"]];
|
||||
NSString *name=descriptor[@"name"];_label=PGUILabel(name,12,NSFontWeightSemibold);[self addSubview:_label];
|
||||
NSString *kind=descriptor[@"kind"];
|
||||
if([descriptor[@"bypass"]boolValue]){
|
||||
_eye=PGUIButton(@"eye",accent,self,@selector(toggleEnabled:));_eye.toolTip=[@"启用 / 停用 · " stringByAppendingString:name];_eye.accessibilityLabel=_eye.toolTip;[self addSubview:_eye];
|
||||
}
|
||||
if([kind isEqual:@"preset"]){
|
||||
NSArray *colors=@[NSNull.null,PGUIColor(.95,.87,.80,1),PGUIColor(.88,.88,.95,1),PGUIColor(1,.86,.72,1),PGUIColor(.99,.78,.75,1),PGUIColor(.76,.57,.39,1),PGUIColor(.48,.32,.22,1)];
|
||||
NSArray *options=descriptor[@"options"] ?: @[];NSMutableArray *buttons=[NSMutableArray new];
|
||||
for(NSUInteger i=0;i<7;i++){
|
||||
PGInspectorGlyphButton *button=PGUIButton(@"swatch",accent,self,@selector(selectPreset:));button.tag=i;button.swatch=i?colors[i]:nil;
|
||||
button.toolTip=i<options.count?options[i]:[NSString stringWithFormat:@"%@ %lu",name,(unsigned long)i];button.accessibilityLabel=button.toolTip;
|
||||
[self addSubview:button];[buttons addObject:button];
|
||||
}_swatches=[buttons copy];
|
||||
}else{
|
||||
_reset=PGUIButton(@"reset",accent,self,@selector(resetValue:));_reset.toolTip=[@"重置 · "stringByAppendingString:name];_reset.accessibilityLabel=_reset.toolTip;[self addSubview:_reset];
|
||||
if([kind isEqual:@"toggle"]){
|
||||
_toggle=PGUIButton(@"switch",accent,self,@selector(toggleValue:));_toggle.accessibilityLabel=name;[self addSubview:_toggle];
|
||||
_hint=PGUILabel(@"",10,NSFontWeightMedium);_hint.textColor=PGUIMuted();_hint.alignment=NSTextAlignmentRight;[self addSubview:_hint];
|
||||
_label.lineBreakMode=NSLineBreakByWordWrapping;_label.maximumNumberOfLines=2;
|
||||
}else{
|
||||
_slider=[[PGInspectorSlider alloc]initWithFrame:NSZeroRect];PGInspectorSliderCell *cell=[PGInspectorSliderCell new];cell.accent=accent;_slider.cell=cell;
|
||||
_slider.minValue=[descriptor[@"min"]doubleValue];_slider.maxValue=[descriptor[@"max"]doubleValue];_slider.continuous=YES;_slider.target=self;_slider.action=@selector(sliderChanged:);_slider.accessibilityLabel=name;
|
||||
__weak PGInspectorView *weakInspector=inspector;
|
||||
_slider.interactionBegan=^{[weakInspector beginControlInteraction];};_slider.interactionEnded=^{[weakInspector endControlInteraction];};[self addSubview:_slider];
|
||||
_valueField=[[PGInspectorNumberField alloc]initWithFrame:NSZeroRect];_valueField.font=[NSFont monospacedDigitSystemFontOfSize:11 weight:NSFontWeightSemibold];_valueField.textColor=PGUIText();_valueField.alignment=NSTextAlignmentCenter;
|
||||
_valueField.bordered=NO;_valueField.bezeled=NO;_valueField.drawsBackground=NO;_valueField.backgroundColor=PGUIColor(.14,.19,.26,1);_valueField.wantsLayer=YES;_valueField.layer.backgroundColor=_valueField.backgroundColor.CGColor;_valueField.layer.cornerRadius=7;_valueField.layer.borderWidth=.7;_valueField.layer.borderColor=[accent colorWithAlphaComponent:.24].CGColor;
|
||||
_valueField.target=self;_valueField.action=@selector(numberCommitted:);_valueField.delegate=self;_valueField.accessibilityLabel=[name stringByAppendingString:@"数值"];[self addSubview:_valueField];
|
||||
}
|
||||
}
|
||||
NSString *key=[descriptor[@"id"]stringValue];
|
||||
_slider.identifier=[@"pg.value."stringByAppendingString:key];_valueField.identifier=[@"pg.number."stringByAppendingString:key];
|
||||
_toggle.identifier=[@"pg.toggle."stringByAppendingString:key];_eye.identifier=[@"pg.enable."stringByAppendingString:key];_reset.identifier=[@"pg.reset."stringByAppendingString:key];
|
||||
}return self;
|
||||
}
|
||||
- (CGFloat)heightForWidth:(CGFloat)width {
|
||||
if(_swatches)return 46;
|
||||
if(_toggle)return width<310 && [_descriptor[@"name"]length]>12 ? 48 : 36;
|
||||
return width<310?52:36;
|
||||
}
|
||||
- (void)layout {
|
||||
[super layout];CGFloat w=NSWidth(self.bounds),h=NSHeight(self.bounds);BOOL narrow=w<310;
|
||||
CGFloat labelX=_eye?25:0;
|
||||
_eye.frame=NSMakeRect(0,6,20,22);
|
||||
if(_swatches){
|
||||
_label.frame=NSMakeRect(labelX,14,33,20);CGFloat x=labelX+37;CGFloat d=MIN(28,MAX(12,(w-x-24)/7));CGFloat gap=MIN(8,MAX(0,(w-x-7*d)/6));
|
||||
for(NSUInteger i=0;i<_swatches.count;i++)_swatches[i].frame=NSMakeRect(x+i*(d+gap),7,d,d);return;
|
||||
}
|
||||
_reset.frame=NSMakeRect(w-23,6,23,23);
|
||||
if(_toggle){
|
||||
CGFloat switchX=w-76;_toggle.frame=NSMakeRect(switchX,4+(h-36)/2,46,28);_hint.frame=NSMakeRect(switchX-39,10+(h-36)/2,34,18);
|
||||
_label.frame=NSMakeRect(labelX,h>36?3:9,MAX(20,switchX-labelX-42),h>36?42:22);return;
|
||||
}
|
||||
CGFloat fieldW=49,fieldX=w-23-7-fieldW;_valueField.frame=NSMakeRect(fieldX,5,fieldW,25);
|
||||
if(narrow){_label.frame=NSMakeRect(labelX,8,MAX(20,fieldX-labelX-6),22);_slider.frame=NSMakeRect(labelX,29,MAX(20,w-labelX-30),18);}
|
||||
else{CGFloat labelW=MIN(116,MAX(71,w*.29));_label.frame=NSMakeRect(labelX,9,labelW,21);CGFloat start=labelX+labelW+5;_slider.frame=NSMakeRect(start,7,MAX(20,fieldX-start-8),22);}
|
||||
}
|
||||
- (void)reloadState:(PGInspectorState *)state master:(BOOL)master available:(BOOL)available {
|
||||
UInt32 identifier=[_descriptor[@"id"]unsignedIntValue];double value=[state valueForParameter:identifier];BOOL enabled=[state isParameterEnabled:identifier];
|
||||
_label.alphaValue=master&&enabled?1:.5;_eye.state=enabled?NSControlStateValueOn:NSControlStateValueOff;_eye.enabled=available&&master;
|
||||
_reset.enabled=available;_reset.state=fabs(value-[_descriptor[@"default"]doubleValue])>.000001?NSControlStateValueOn:NSControlStateValueOff;
|
||||
_slider.enabled=available&&master&&enabled;_slider.doubleValue=value;
|
||||
_valueField.enabled=available&&master&&enabled;if(!_valueField.currentEditor || !_numberHasUnsubmittedEdit)_valueField.stringValue=PGFormattedValue(_descriptor,value);
|
||||
_toggle.enabled=available&&master&&enabled;_toggle.state=value>.5?NSControlStateValueOn:NSControlStateValueOff;
|
||||
UInt32 identifierForHint=[_descriptor[@"id"]unsignedIntValue];
|
||||
_hint.stringValue=identifierForHint==PGFastMulti?(value>.5?@"多人":@"单人"):identifierForHint==PGTrackBoxes?(value>.5?@"预览中":@"隐藏"):(value>.5?@"开启":@"关闭");
|
||||
for(NSUInteger i=0;i<_swatches.count;i++){_swatches[i].enabled=available&&master&&enabled;_swatches[i].state=i==(NSUInteger)llround(value)?NSControlStateValueOn:NSControlStateValueOff;}
|
||||
}
|
||||
- (void)sliderChanged:(NSSlider *)sender { [_inspector writeValue:sender.doubleValue parameter:[_descriptor[@"id"]unsignedIntValue]]; }
|
||||
- (void)toggleValue:(NSButton *)sender { [_inspector writeValue:sender.state==NSControlStateValueOn?1:0 parameter:[_descriptor[@"id"]unsignedIntValue]]; }
|
||||
- (void)selectPreset:(NSButton *)sender { [_inspector writeValue:sender.tag parameter:[_descriptor[@"id"]unsignedIntValue]]; }
|
||||
- (void)toggleEnabled:(id)sender { (void)sender;[_inspector.controller toggleEnabledForParameter:[_descriptor[@"id"]unsignedIntValue]];[_inspector reloadControls]; }
|
||||
- (void)resetValue:(id)sender { (void)sender;[_inspector.controller resetParameter:[_descriptor[@"id"]unsignedIntValue]];[_inspector reloadControls]; }
|
||||
- (void)numberCommitted:(id)sender {
|
||||
// The explicit Return action and the subsequent end-editing notification
|
||||
// describe the same submission. Clear the dirty flag before host callbacks
|
||||
// so focus loss cannot write stale text over a reset or a host undo.
|
||||
_numberHasUnsubmittedEdit=NO;
|
||||
NSString *text=[_valueField.stringValue stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];
|
||||
if([text hasSuffix:@"%"]||[text hasSuffix:@"°"])text=[text substringToIndex:text.length-1];
|
||||
NSNumber *number=nil;
|
||||
for(NSLocale *locale in @[NSLocale.currentLocale,[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]]){
|
||||
NSScanner *scanner=[NSScanner scannerWithString:text];scanner.locale=locale;double parsed;
|
||||
if([scanner scanDouble:&parsed]&&scanner.isAtEnd){number=@(parsed);break;}
|
||||
}
|
||||
if(number&&isfinite(number.doubleValue)){
|
||||
double value=number.doubleValue/PGValueScale(_descriptor);value=fmin([_descriptor[@"max"]doubleValue],fmax([_descriptor[@"min"]doubleValue],value));
|
||||
// Commit explicit input even when it equals our last displayed value:
|
||||
// the document may have changed while the field editor held focus.
|
||||
// The controller rereads the host and deduplicates the actual value.
|
||||
[_inspector writeValue:value parameter:[_descriptor[@"id"]unsignedIntValue]];
|
||||
}else NSBeep();
|
||||
_valueField.stringValue=PGFormattedValue(_descriptor,[_inspector.controller.state valueForParameter:[_descriptor[@"id"]unsignedIntValue]]);
|
||||
if(sender==_valueField) {
|
||||
[_valueField.window makeFirstResponder:nil];
|
||||
// A remote inspector can retain its field editor after the host has
|
||||
// consumed Return. End the already-submitted edit at the control too.
|
||||
[_valueField abortEditing];
|
||||
}
|
||||
}
|
||||
- (void)controlTextDidChange:(NSNotification *)notification { (void)notification;_numberHasUnsubmittedEdit=YES; }
|
||||
- (void)controlTextDidEndEditing:(NSNotification *)notification {
|
||||
(void)notification;
|
||||
if(_numberHasUnsubmittedEdit)[self numberCommitted:nil];
|
||||
[_inspector refreshFromHost];
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation PGInspectorCard
|
||||
- (BOOL)isFlipped { return YES; }
|
||||
- (CGFloat)heightForWidth:(CGFloat)width {
|
||||
if(!_expanded)return 44;CGFloat height=49;for(PGInspectorRow *row in _rows){height+=[row heightForWidth:MAX(0,width-24)];if([row.descriptor[@"id"]unsignedIntValue]==PGJumpGuard)height+=8;}return height+7;
|
||||
}
|
||||
- (void)layout {
|
||||
[super layout];CGFloat w=NSWidth(self.bounds);_titleLabel.frame=NSMakeRect(25,13,MAX(70,w-80),22);_master.frame=NSMakeRect(w-39,10,25,25);
|
||||
CGFloat y=49;for(PGInspectorRow *row in _rows){row.hidden=!_expanded;if(!_expanded)continue;CGFloat h=[row heightForWidth:w-24];row.frame=NSMakeRect(12,y,w-24,h);y+=h;if([row.descriptor[@"id"]unsignedIntValue]==PGJumpGuard)y+=8;}
|
||||
}
|
||||
- (void)drawRect:(NSRect)dirtyRect {
|
||||
(void)dirtyRect;NSBezierPath *shape=[NSBezierPath bezierPathWithRoundedRect:NSInsetRect(self.bounds,.5,.5) xRadius:13 yRadius:13];
|
||||
[NSGraphicsContext saveGraphicsState];[shape addClip];NSGradient *gradient=[[NSGradient alloc]initWithStartingColor:[_accent blendedColorWithFraction:.87 ofColor:PGUIColor(.09,.14,.21,1)] endingColor:PGUIColor(.055,.085,.14,1)];[gradient drawInRect:self.bounds angle:90];[NSGraphicsContext restoreGraphicsState];
|
||||
[[_accent colorWithAlphaComponent:.22]setStroke];shape.lineWidth=.8;[shape stroke];
|
||||
NSRect pill=NSMakeRect(12,10,MAX(53,[_heading length]*13+20),25);[[_accent colorWithAlphaComponent:.10]setFill];NSBezierPath *p=[NSBezierPath bezierPathWithRoundedRect:pill xRadius:8 yRadius:8];[p fill];[[_accent colorWithAlphaComponent:.32]setStroke];p.lineWidth=.7;[p stroke];
|
||||
if(_expanded){[[PGUIMuted() colorWithAlphaComponent:.20]setStroke];NSBezierPath *line=[NSBezierPath bezierPath];[line moveToPoint:NSMakePoint(12,43)];[line lineToPoint:NSMakePoint(NSWidth(self.bounds)-12,43)];line.lineWidth=.5;[line stroke];
|
||||
for(PGInspectorRow *row in _rows)if([row.descriptor[@"id"]unsignedIntValue]==PGJumpGuard){CGFloat y=NSMaxY(row.frame)+4;[line removeAllPoints];[line moveToPoint:NSMakePoint(12,y)];[line lineToPoint:NSMakePoint(NSWidth(self.bounds)-12,y)];[line stroke];}
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation PGInspectorView {
|
||||
NSArray<PGInspectorCard *> *_cards;
|
||||
PGInspectorCard *_more;
|
||||
NSTextField *_brand,*_website,*_status;
|
||||
NSTimer *_refreshTimer;
|
||||
NSUInteger _interactionDepth;
|
||||
}
|
||||
- (instancetype)initWithAPIManager:(id<PROAPIAccessing>)manager { return [self initWithController:[[PGInspectorController alloc]initWithAPIManager:manager]]; }
|
||||
- (instancetype)initWithController:(PGInspectorController *)controller {
|
||||
if((self=[super initWithFrame:NSMakeRect(0,0,320,100)])){
|
||||
_controller=controller;_controller.actionSender=self;self.appearance=[NSAppearance appearanceNamed:NSAppearanceNameDarkAqua];
|
||||
NSArray *sections=@[@"smooth",@"color",@"shape",@"more"],*headings=@[@"磨皮",@"美白",@"瘦脸",@"更多设置"];
|
||||
NSArray *colors=@[PGUIColor(.28,.83,.96,1),PGUIColor(.34,.89,.84,1),PGUIColor(.59,.69,.98,1),PGUIColor(.54,.64,.76,1)];
|
||||
NSMutableArray *cards=[NSMutableArray new];
|
||||
for(NSUInteger index=0;index<sections.count;index++){
|
||||
NSString *section=sections[index];PGInspectorCard *card=[[PGInspectorCard alloc]initWithFrame:NSZeroRect];card.accent=colors[index];card.heading=headings[index];card.expanded=index<3;
|
||||
card.titleLabel=PGUILabel(headings[index],12,NSFontWeightSemibold);card.titleLabel.textColor=colors[index];[card addSubview:card.titleLabel];NSMutableArray *rows=[NSMutableArray new];
|
||||
for(NSDictionary *descriptor in PGInspectorControls()){
|
||||
if(![descriptor[@"section"]isEqual:section])continue;
|
||||
if([descriptor[@"kind"]isEqual:@"master"]){card.master=PGUIButton(@"eye",colors[index],self,@selector(masterChanged:));card.master.tag=[descriptor[@"id"]integerValue];card.master.identifier=[NSString stringWithFormat:@"pg.master.%@",descriptor[@"id"]];card.master.accessibilityLabel=descriptor[@"name"];card.master.toolTip=descriptor[@"name"];[card addSubview:card.master];continue;}
|
||||
PGInspectorRow *row=[[PGInspectorRow alloc]initWithDescriptor:descriptor accent:colors[index] inspector:self];[rows addObject:row];[card addSubview:row];
|
||||
}
|
||||
card.rows=rows;if(index==3){_more=card;card.master=PGUIButton(@"disclosure",colors[index],self,@selector(moreChanged:));card.master.identifier=@"pg.more";card.master.accessibilityLabel=@"展开更多设置";[card addSubview:card.master];}
|
||||
[self addSubview:card];[cards addObject:card];
|
||||
}_cards=[cards copy];
|
||||
_brand=PGUILabel(@"大鱼 · 人像美化",15,NSFontWeightSemibold);_brand.alignment=NSTextAlignmentCenter;[self addSubview:_brand];
|
||||
_website=PGUILabel(@"dayu.media",10,NSFontWeightMedium);_website.alignment=NSTextAlignmentCenter;_website.textColor=PGUIMuted();[self addSubview:_website];
|
||||
_status=PGUILabel(@"",10,NSFontWeightRegular);_status.textColor=PGUIMuted();_status.alignment=NSTextAlignmentCenter;[self addSubview:_status];
|
||||
[self reloadControls];[self setFrameSize:NSMakeSize(320,[self heightForWidth:320])];
|
||||
}return self;
|
||||
}
|
||||
- (BOOL)isFlipped { return YES; }
|
||||
- (CGFloat)heightForWidth:(CGFloat)width { CGFloat h=12;for(PGInspectorCard *card in _cards)h+=[card heightForWidth:MAX(0,width-20)]+10;return h+83; }
|
||||
- (NSSize)intrinsicContentSize { return NSMakeSize(NSViewNoIntrinsicMetric,[self heightForWidth:NSWidth(self.bounds)>0?NSWidth(self.bounds):320]); }
|
||||
- (NSSize)fittingSize { CGFloat w=NSWidth(self.bounds)>0?NSWidth(self.bounds):320;return NSMakeSize(w,[self heightForWidth:w]); }
|
||||
- (void)setFrameSize:(NSSize)newSize { if(_cards)newSize.height=[self heightForWidth:newSize.width];[super setFrameSize:newSize];[self invalidateIntrinsicContentSize];self.needsLayout=YES; }
|
||||
- (void)layout {
|
||||
[super layout];CGFloat w=NSWidth(self.bounds),y=12;for(PGInspectorCard *card in _cards){CGFloat h=[card heightForWidth:w-20];card.frame=NSMakeRect(10,y,MAX(0,w-20),h);y+=h+10;}
|
||||
_brand.frame=NSMakeRect(12,y+12,MAX(0,w-24),23);_website.frame=NSMakeRect(12,y+38,MAX(0,w-24),17);_status.frame=NSMakeRect(12,y+61,MAX(0,w-24),16);
|
||||
}
|
||||
- (void)drawRect:(NSRect)dirtyRect { [PGUIColor(.035,.060,.105,1)setFill];NSRectFill(dirtyRect); }
|
||||
- (void)reloadControls {
|
||||
PGInspectorState *state=_controller.state;BOOL available=_controller.available;
|
||||
for(PGInspectorCard *card in _cards){BOOL master=card==_more || [state valueForParameter:(UInt32)card.master.tag]>.5;
|
||||
if(card!=_more){card.master.state=master?NSControlStateValueOn:NSControlStateValueOff;card.master.enabled=available;}
|
||||
for(PGInspectorRow *row in card.rows)[row reloadState:state master:master available:available];
|
||||
}
|
||||
_status.stringValue=_controller.statusMessage.length?_controller.statusMessage:@"自动跟随 · 无需手动分析";_status.toolTip=_status.stringValue;
|
||||
}
|
||||
- (BOOL)hasActiveTextEditor { for(PGInspectorCard *card in _cards)for(PGInspectorRow *row in card.rows)if(row.valueField.currentEditor && row.numberHasUnsubmittedEdit)return YES;return NO; }
|
||||
- (void)refreshFromHost { if(_interactionDepth || [self hasActiveTextEditor])return;[_controller refresh];[self reloadControls]; }
|
||||
- (void)hostParameterChanged { if(_interactionDepth || [self hasActiveTextEditor])return;[_controller documentDidChange];[self reloadControls]; }
|
||||
- (void)writeValue:(double)value parameter:(UInt32)identifier { [_controller setValue:value forParameter:identifier];[self reloadControls]; }
|
||||
// Keep refreshes out of the tracking loop, but let writeValue own a complete
|
||||
// FxPlug action for each continuous step. Holding the action until mouse-up
|
||||
// prevents hosts that publish at endAction from previewing intermediate values.
|
||||
- (void)beginControlInteraction { _interactionDepth++; }
|
||||
- (void)endControlInteraction { if(!_interactionDepth)return;if(--_interactionDepth==0)[self refreshFromHost]; }
|
||||
- (void)masterChanged:(NSButton *)sender { [_controller setValue:sender.state==NSControlStateValueOn?1:0 forParameter:(UInt32)sender.tag];[self reloadControls]; }
|
||||
- (void)moreChanged:(NSButton *)sender { _more.expanded=sender.state==NSControlStateValueOn;sender.accessibilityLabel=_more.expanded?@"收起更多设置":@"展开更多设置";[_more setNeedsDisplay:YES];[self setFrameSize:self.frame.size]; }
|
||||
- (void)viewDidMoveToWindow {
|
||||
[super viewDidMoveToWindow];[_refreshTimer invalidate];_refreshTimer=nil;
|
||||
if(self.window){[self refreshFromHost];__weak PGInspectorView *weakSelf=self;_refreshTimer=[NSTimer timerWithTimeInterval:.65 repeats:YES block:^(NSTimer *timer){PGInspectorView *view=weakSelf;if(!view||!view.window){[timer invalidate];return;}[view refreshFromHost];}];[[NSRunLoop mainRunLoop]addTimer:_refreshTimer forMode:NSRunLoopCommonModes];}
|
||||
}
|
||||
- (void)dealloc { [_refreshTimer invalidate]; }
|
||||
@end
|
||||
@@ -13,5 +13,6 @@ enum {
|
||||
PGAnalyzeForward=50, PGAnalyzeBackward=51, PGClearTrack=52, PGStatus=53,
|
||||
PGTrackData1=60, PGTrackData2=61, PGTrackData3=62,
|
||||
PGSmoothingEnabled=70, PGColorEnabled=71, PGShapeEnabled=72,
|
||||
PGDetailEnabled=73, PGHelp=74
|
||||
PGDetailEnabled=73, PGHelp=74,
|
||||
PGInspectorPanel=210
|
||||
};
|
||||
@@ -2,7 +2,7 @@
|
||||
#import <FxPlug/FxPlugSDK.h>
|
||||
#import "PGBeautyTypes.h"
|
||||
|
||||
@interface beatygrilPlugIn : NSObject <FxTileableEffect, FxAnalyzer>
|
||||
@interface beatygrilPlugIn : NSObject <FxTileableEffect, FxAnalyzer, FxCustomParameterViewHost_v2>
|
||||
@property (nonatomic, assign) id<PROAPIAccessing> apiManager;
|
||||
- (PGBeautySettings)settingsAtTime:(CMTime)time;
|
||||
- (void)analyzeForward;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#import "PGParameterIDs.h"
|
||||
#import "PGBeautyRenderer.h"
|
||||
#import "PGFaceProcessor.h"
|
||||
#import "PGInspectorState.h"
|
||||
#import "PGInspectorView.h"
|
||||
#import <Metal/Metal.h>
|
||||
#import <IOSurface/IOSurface.h>
|
||||
#import <stddef.h>
|
||||
@@ -90,8 +92,8 @@ static CIImage *PGImageFromTile(FxImageTile *tile) {
|
||||
@property BOOL analyzing;
|
||||
@property BOOL analysisForward;
|
||||
@property (strong) NSArray<NSString *> *loadedTracks;
|
||||
@property uint32_t compatibleTrackMask;
|
||||
@property BOOL reportedRender;
|
||||
@property (strong) NSHashTable<PGInspectorView *> *inspectorViews;
|
||||
@end
|
||||
|
||||
@implementation beatygrilPlugIn
|
||||
@@ -102,6 +104,7 @@ static CIImage *PGImageFromTile(FxImageTile *tile) {
|
||||
_analysisFaceProcessor=[PGFaceProcessor new];
|
||||
_contexts=[NSMutableDictionary new]; _analysisSettings=PGBeautyDefaults();
|
||||
_analysisTime=kCMTimeZero;
|
||||
_inspectorViews=[NSHashTable weakObjectsHashTable];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -120,8 +123,8 @@ static CIImage *PGImageFromTile(FxImageTile *tile) {
|
||||
PGSlider spec=PGSliders[i]; if (spec.identifier!=identifier) continue;
|
||||
NSString *name=[NSString stringWithUTF8String:spec.name];
|
||||
float value=*(float *)((uint8_t *)&defaults+spec.offset);
|
||||
if (spec.percent) return [api addPercentSliderWithName:name parameterID:identifier defaultValue:value parameterMin:spec.min parameterMax:spec.max sliderMin:spec.min sliderMax:spec.max delta:spec.step parameterFlags:kFxParameterFlag_DEFAULT];
|
||||
return [api addFloatSliderWithName:name parameterID:identifier defaultValue:value parameterMin:spec.min parameterMax:spec.max sliderMin:spec.min sliderMax:spec.max delta:spec.step parameterFlags:kFxParameterFlag_DEFAULT];
|
||||
if (spec.percent) return [api addPercentSliderWithName:name parameterID:identifier defaultValue:value parameterMin:spec.min parameterMax:spec.max sliderMin:spec.min sliderMax:spec.max delta:spec.step parameterFlags:kFxParameterFlag_HIDDEN];
|
||||
return [api addFloatSliderWithName:name parameterID:identifier defaultValue:value parameterMin:spec.min parameterMax:spec.max sliderMin:spec.min sliderMax:spec.max delta:spec.step parameterFlags:kFxParameterFlag_HIDDEN];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
@@ -129,37 +132,56 @@ static CIImage *PGImageFromTile(FxImageTile *tile) {
|
||||
id<FxParameterCreationAPI_v5> api=[_apiManager apiForProtocol:@protocol(FxParameterCreationAPI_v5)];
|
||||
if (!api) return PGFail(error,@"无法取得 FxPlug 参数创建接口。");
|
||||
__block BOOL ok=YES;
|
||||
ok=[api addCustomParameterWithName:@"" parameterID:PGInspectorPanel
|
||||
defaultValue:[PGInspectorState stateWithValues:@{} disabledParameters:[NSSet set] hasEdits:NO]
|
||||
parameterFlags:kFxParameterFlag_CUSTOM_UI|kFxParameterFlag_USE_FULL_VIEW_WIDTH]&&ok;
|
||||
void (^slider)(UInt32)=^(UInt32 identifier){ok=[self addSlider:identifier api:api]&&ok;};
|
||||
void (^toggle)(NSString *,UInt32,BOOL)=^(NSString *name,UInt32 identifier,BOOL value){ok=[api addToggleButtonWithName:name parameterID:identifier defaultValue:value parameterFlags:kFxParameterFlag_DEFAULT]&&ok;};
|
||||
void (^group)(NSString *,UInt32)=^(NSString *name,UInt32 identifier){ok=[api startParameterSubGroup:name parameterID:identifier parameterFlags:kFxParameterFlag_DEFAULT]&&ok;};
|
||||
void (^toggle)(NSString *,UInt32,BOOL)=^(NSString *name,UInt32 identifier,BOOL value){ok=[api addToggleButtonWithName:name parameterID:identifier defaultValue:value parameterFlags:kFxParameterFlag_HIDDEN]&&ok;};
|
||||
void (^group)(NSString *,UInt32)=^(NSString *name,UInt32 identifier){ok=[api startParameterSubGroup:name parameterID:identifier parameterFlags:kFxParameterFlag_HIDDEN]&&ok;};
|
||||
void (^end)(void)=^{ok=[api endParameterSubGroup]&&ok;};
|
||||
group(@"磨皮",100); toggle(@"启用磨皮",PGSmoothingEnabled,YES);
|
||||
slider(PGAmount); slider(PGMidtone); slider(PGRadius); toggle(@"保护五官",PGFeatureProtect,YES); end();
|
||||
group(@"肤色",101); toggle(@"启用肤色",PGColorEnabled,YES);
|
||||
ok=[api addPopupMenuWithName:@"肤色风格" parameterID:PGTonePreset defaultValue:0 menuEntries:@[@"原色",@"自然白",@"冷白",@"暖白",@"红润白",@"小麦",@"古铜"] parameterFlags:0]&&ok;
|
||||
ok=[api addPopupMenuWithName:@"肤色风格" parameterID:PGTonePreset defaultValue:0 menuEntries:@[@"原色",@"自然白",@"冷白",@"暖白",@"红润白",@"小麦",@"古铜"] parameterFlags:kFxParameterFlag_HIDDEN]&&ok;
|
||||
slider(PGBrightness); slider(PGHue); slider(PGSaturation); slider(PGEvenTone); slider(PGTeeth); slider(PGEyeBright); end();
|
||||
group(@"细节",102); toggle(@"启用细节",PGDetailEnabled,YES); slider(PGHighPass); slider(PGSharpness); end();
|
||||
group(@"脸型",103); toggle(@"启用脸型",PGShapeEnabled,YES);
|
||||
UInt32 shape[]={PGThin,PGBigEye,PGShort,PGNarrow,PGChin,PGVFace,PGJaw,PGCheekbone,PGSmallHead,PGSmallHeadRange};
|
||||
for(NSUInteger i=0;i<sizeof(shape)/sizeof(UInt32);i++) slider(shape[i]); end();
|
||||
group(@"人脸选择与跟踪",104); toggle(@"只处理选中人脸",PGSingleFace,NO);
|
||||
ok=[api addPointParameterWithName:@"选脸位置" parameterID:PGSelection defaultX:.5 defaultY:.5 parameterFlags:0]&&ok;
|
||||
ok=[api addPointParameterWithName:@"选脸位置" parameterID:PGSelection defaultX:.5 defaultY:.5 parameterFlags:kFxParameterFlag_HIDDEN]&&ok;
|
||||
toggle(@"多人高速模式",PGFastMulti,NO); slider(PGJumpGuard);
|
||||
toggle(@"使用跟踪数据",PGTrackEnabled,NO);
|
||||
ok=[api addPopupMenuWithName:@"跟踪目标" parameterID:PGTarget defaultValue:0 menuEntries:@[@"目标 1",@"目标 2",@"目标 3"] parameterFlags:kFxParameterFlag_NOT_ANIMATABLE]&&ok;
|
||||
ok=[api addPushButtonWithName:@"向前分析" parameterID:PGAnalyzeForward selector:@selector(analyzeForward) parameterFlags:0]&&ok;
|
||||
ok=[api addPushButtonWithName:@"向后分析" parameterID:PGAnalyzeBackward selector:@selector(analyzeBackward) parameterFlags:0]&&ok;
|
||||
ok=[api addPushButtonWithName:@"清除当前目标" parameterID:PGClearTrack selector:@selector(clearTracking) parameterFlags:0]&&ok;
|
||||
ok=[api addStringParameterWithName:@"跟踪状态" parameterID:PGStatus defaultValue:@"选择画面中的人脸后开始分析" parameterFlags:kFxParameterFlag_NOT_ANIMATABLE|kFxParameterFlag_DISABLED]&&ok;
|
||||
ok=[api addPopupMenuWithName:@"跟踪目标" parameterID:PGTarget defaultValue:0 menuEntries:@[@"目标 1",@"目标 2",@"目标 3"] parameterFlags:kFxParameterFlag_NOT_ANIMATABLE|kFxParameterFlag_HIDDEN]&&ok;
|
||||
ok=[api addPushButtonWithName:@"向前分析" parameterID:PGAnalyzeForward selector:@selector(analyzeForward) parameterFlags:kFxParameterFlag_HIDDEN|kFxParameterFlag_DISABLED]&&ok;
|
||||
ok=[api addPushButtonWithName:@"向后分析" parameterID:PGAnalyzeBackward selector:@selector(analyzeBackward) parameterFlags:kFxParameterFlag_HIDDEN|kFxParameterFlag_DISABLED]&&ok;
|
||||
ok=[api addPushButtonWithName:@"清除当前目标" parameterID:PGClearTrack selector:@selector(clearTracking) parameterFlags:kFxParameterFlag_HIDDEN|kFxParameterFlag_DISABLED]&&ok;
|
||||
ok=[api addStringParameterWithName:@"跟踪状态" parameterID:PGStatus defaultValue:@"自动人脸跟随" parameterFlags:kFxParameterFlag_NOT_ANIMATABLE|kFxParameterFlag_DISABLED|kFxParameterFlag_HIDDEN]&&ok;
|
||||
toggle(@"显示人脸框",PGFaceBoxes,NO); toggle(@"显示跟踪框",PGTrackBoxes,NO); end();
|
||||
group(@"蒙版与输出",105); slider(PGThreshold); slider(PGSoftness); slider(PGEdgeProtect);
|
||||
toggle(@"预览蒙版",PGShowMask,NO); toggle(@"反转蒙版",PGInvertMask,NO);
|
||||
ok=[api addPopupMenuWithName:@"蒙版通道" parameterID:PGDebugChannel defaultValue:0 menuEntries:@[@"皮肤",@"牙齿",@"眼睛"] parameterFlags:0]&&ok;
|
||||
ok=[api addPopupMenuWithName:@"蒙版通道" parameterID:PGDebugChannel defaultValue:0 menuEntries:@[@"皮肤",@"牙齿",@"眼睛"] parameterFlags:kFxParameterFlag_HIDDEN]&&ok;
|
||||
slider(PGMix); end();
|
||||
for(UInt32 identifier=PGTrackData1;identifier<=PGTrackData3;identifier++)
|
||||
ok=[api addStringParameterWithName:@"分析数据" parameterID:identifier defaultValue:@"" parameterFlags:kFxParameterFlag_HIDDEN|kFxParameterFlag_NOT_ANIMATABLE]&&ok;
|
||||
return ok ? YES : PGFail(error,@"宿主未能创建完整的人像美化参数。");
|
||||
}
|
||||
- (NSView *)createViewForParameterID:(UInt32)identifier {
|
||||
if(identifier!=PGInspectorPanel)return nil;
|
||||
PGInspectorView *view=[[PGInspectorView alloc] initWithAPIManager:_apiManager];
|
||||
@synchronized(_inspectorViews){[_inspectorViews addObject:view];}return view;
|
||||
}
|
||||
- (BOOL)parameterChanged:(UInt32)identifier atTime:(CMTime)time error:(NSError **)error {
|
||||
// The host callback covers undo and playhead-driven parameter changes. Hop
|
||||
// to AppKit's thread and avoid retaining remote inspector views in render.
|
||||
NSArray *views;@synchronized(_inspectorViews){views=_inspectorViews.allObjects;}
|
||||
for(PGInspectorView *view in views)dispatch_async(dispatch_get_main_queue(), ^{[view hostParameterChanged];});
|
||||
return YES;
|
||||
}
|
||||
- (Class)classForCustomParameterID:(UInt32)identifier {return identifier==PGInspectorPanel?PGInspectorState.class:Nil;}
|
||||
- (NSSet<Class> *)classesForCustomParameterID:(UInt32)identifier {
|
||||
return identifier==PGInspectorPanel?[NSSet setWithObjects:PGInspectorState.class,NSDictionary.class,NSNumber.class,NSSet.class,nil]:[NSSet set];
|
||||
}
|
||||
- (PGBeautySettings)settingsAtTime:(CMTime)time {
|
||||
PGBeautySettings s=PGBeautyDefaults();
|
||||
id<FxParameterRetrievalAPI_v6> api=[_apiManager apiForProtocol:@protocol(FxParameterRetrievalAPI_v6)];
|
||||
@@ -192,6 +214,13 @@ static CIImage *PGImageFromTile(FxImageTile *tile) {
|
||||
enabled=YES; [api getBoolValue:&enabled fromParameter:PGShapeEnabled atTime:time];
|
||||
if(!enabled) s.faceThin=s.faceBigEye=s.faceShort=s.faceNarrow=s.faceChin=s.faceV=s.faceJaw=s.faceCheekbone=s.faceSmallHead=0;
|
||||
if(s.fastMultiFace) s.singleFace=0;
|
||||
NSObject *panel=nil;
|
||||
[api getCustomParameterValue:&panel fromParameter:PGInspectorPanel atTime:time];
|
||||
if([panel isKindOfClass:PGInspectorState.class])s=PGSettingsWithInspectorState(s,(PGInspectorState *)panel);
|
||||
// The current product follows faces automatically. Old manual-analysis and
|
||||
// debug values remain saved for document compatibility, but cannot silently
|
||||
// override live geometry or leave an invisible mask/selection mode active.
|
||||
s.trackingEnabled=0;s.singleFace=0;s.showMask=0;s.invertMask=0;s.debugChannel=0;s.showFaceBoxes=0;
|
||||
return s;
|
||||
}
|
||||
- (NSArray<NSString *> *)trackStrings {
|
||||
@@ -207,7 +236,7 @@ static CIImage *PGImageFromTile(FxImageTile *tile) {
|
||||
if(!state) return PGFail(error,@"缺少渲染状态输出位置。");
|
||||
if(![_apiManager apiForProtocol:@protocol(FxParameterRetrievalAPI_v6)]) return PGFail(error,@"无法读取宿主参数。");
|
||||
PGBeautySettings s=[self settingsAtTime:time];
|
||||
*state=[NSPropertyListSerialization dataWithPropertyList:@{@"settings":[NSData dataWithBytes:&s length:sizeof(s)],@"tracks":[self trackStrings]} format:NSPropertyListBinaryFormat_v1_0 options:0 error:error];
|
||||
*state=[NSPropertyListSerialization dataWithPropertyList:@{@"settings":[NSData dataWithBytes:&s length:sizeof(s)],@"tracks":@[@"",@"",@""]} format:NSPropertyListBinaryFormat_v1_0 options:0 error:error];
|
||||
return *state!=nil;
|
||||
}
|
||||
- (BOOL)destinationImageRect:(FxRect *)rect sourceImages:(NSArray<FxImageTile *> *)sources destinationImage:(FxImageTile *)destination pluginState:(NSData *)state atTime:(CMTime)time error:(NSError **)error {
|
||||
@@ -237,6 +266,7 @@ static CIImage *PGImageFromTile(FxImageTile *tile) {
|
||||
if(![plist isKindOfClass:NSDictionary.class]||![plist[@"settings"] isKindOfClass:NSData.class]||[plist[@"settings"] length]!=sizeof(PGBeautySettings)) return PGFail(error,@"渲染状态版本无效。");
|
||||
PGBeautySettings s;[plist[@"settings"] getBytes:&s length:sizeof(s)];
|
||||
if(s.version!=1) return PGFail(error,@"此版本不支持保存的渲染状态。");
|
||||
s.trackingEnabled=0;s.singleFace=0;
|
||||
CIImage *input=PGImageFromTile(sources.firstObject); if(!input) return PGFail(error,@"无法读取 IOSurface。");
|
||||
@synchronized(self) {
|
||||
if(!_reportedRender) {
|
||||
@@ -255,21 +285,6 @@ static CIImage *PGImageFromTile(FxImageTile *tile) {
|
||||
NSArray *tracks=plist[@"tracks"];
|
||||
if(![tracks isKindOfClass:NSArray.class]||tracks.count!=3) return PGFail(error,@"跟踪状态无效。");
|
||||
for(id track in tracks) if(![track isKindOfClass:NSString.class]) return PGFail(error,@"跟踪记录无效。");
|
||||
if(![_loadedTracks isEqualToArray:tracks]) {
|
||||
_compatibleTrackMask=0;
|
||||
for(NSUInteger i=0;i<3;i++) {
|
||||
NSData *data=[[NSData alloc] initWithBase64EncodedString:tracks[i] options:0];
|
||||
BOOL compatible=PGCompatibleHostTrack(data);
|
||||
if(compatible) _compatibleTrackMask |= 1u<<i;
|
||||
[_faceProcessor setTrackingData:compatible?data:nil forTarget:i];
|
||||
}
|
||||
_loadedTracks=[tracks copy];
|
||||
}
|
||||
// Previous releases did not record which image-axis contract their
|
||||
// analysis used. Never replay potentially inverted coordinates.
|
||||
// Preserve the user's saved data and use realtime follow until they
|
||||
// reanalyze that target under this coordinate contract.
|
||||
if(s.trackingEnabled && !(_compatibleTrackMask & (1u<<MIN(s.targetIndex,2)))) s.trackingEnabled=0;
|
||||
NSError *stageError=nil;
|
||||
PGFaceFrame *frame=[_faceProcessor processImage:input settings:s time:time error:&stageError];
|
||||
if(!frame) { os_log_error(PGRenderLog(), "face processing failed: %{public}@",stageError); if(error)*error=stageError; return NO; }
|
||||
|
||||
@@ -1,34 +1,45 @@
|
||||
#import "AppDelegate.h"
|
||||
#import <CoreServices/CoreServices.h>
|
||||
|
||||
@interface AppDelegate ()
|
||||
@property (weak) IBOutlet NSWindow *window;
|
||||
@property (strong) NSTimer *exitTimer;
|
||||
@end
|
||||
|
||||
@implementation AppDelegate
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
|
||||
self.window.title=@"大鱼人像美化";
|
||||
[self.window setContentSize:NSMakeSize(600,360)];
|
||||
NSView *content=[NSView new];self.window.contentView=content;
|
||||
NSStackView *stack=[NSStackView new];stack.orientation=NSUserInterfaceLayoutOrientationVertical;
|
||||
stack.alignment=NSLayoutAttributeLeading;stack.spacing=16;stack.translatesAutoresizingMaskIntoConstraints=NO;
|
||||
[content addSubview:stack];
|
||||
[NSLayoutConstraint activateConstraints:@[[stack.leadingAnchor constraintEqualToAnchor:content.leadingAnchor constant:32],[stack.trailingAnchor constraintEqualToAnchor:content.trailingAnchor constant:-32],[stack.topAnchor constraintEqualToAnchor:content.topAnchor constant:30]]];
|
||||
NSTextField *title=[NSTextField labelWithString:@"大鱼人像美化"];
|
||||
title.font=[NSFont systemFontOfSize:28 weight:NSFontWeightSemibold];[stack addArrangedSubview:title];
|
||||
NSTextField *subtitle=[NSTextField labelWithString:@"beatygril · FxPlug 4"];
|
||||
subtitle.textColor=NSColor.secondaryLabelColor;[stack addArrangedSubview:subtitle];
|
||||
NSTextField *description=[NSTextField wrappingLabelWithString:@"在 Motion 的滤镜资源库中选择「大鱼 → 大鱼人像美化」。Final Cut Pro 的效果浏览器中也可使用配套效果。\n\n美化会随画面人脸自动更新,无需先点分析。脸型默认跟随主要人脸,多人模式可处理多张脸。启用「只处理选中人脸」后,可点击画面更换目标。向前/向后分析为可选功能,结果可随工程保存。"];
|
||||
description.font=[NSFont systemFontOfSize:13];[stack addArrangedSubview:description];
|
||||
NSStackView *buttons=[NSStackView new];buttons.orientation=NSUserInterfaceLayoutOrientationHorizontal;buttons.spacing=12;
|
||||
[buttons addArrangedSubview:[NSButton buttonWithTitle:@"打开 Motion" target:self action:@selector(openMotion:)]];
|
||||
[buttons addArrangedSubview:[NSButton buttonWithTitle:@"打开 Final Cut Pro" target:self action:@selector(openFinalCut:)]];
|
||||
[stack addArrangedSubview:buttons];
|
||||
NSTextField *note=[NSTextField wrappingLabelWithString:@"建议先调高中频磨皮,再按需调节肤色与脸型。肤色风格为本插件独立调色预设。"];
|
||||
note.textColor=NSColor.secondaryLabelColor;note.font=[NSFont systemFontOfSize:11];[stack addArrangedSubview:note];
|
||||
self.window.title = @"大鱼人像美化 · 免费开源";
|
||||
[self.window setContentSize:NSMakeSize(800, 450)];
|
||||
self.window.styleMask &= ~NSWindowStyleMaskResizable;
|
||||
self.window.backgroundColor = [NSColor colorWithRed:0.94 green:0.92 blue:0.85 alpha:1];
|
||||
NSImageView *card = [[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, 800, 450)];
|
||||
card.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
|
||||
card.imageScaling = NSImageScaleProportionallyUpOrDown;
|
||||
NSURL *artwork = [NSBundle.mainBundle URLForResource:@"LauncherInfo" withExtension:@"png"];
|
||||
card.image = [[NSImage alloc] initWithContentsOfURL:artwork];
|
||||
card.accessibilityLabel = @"大鱼人像美化,免费开源,自动注册,无需激活码。主程序启动三秒后自动退出;Final Cut Pro 和 Motion 中的滤镜继续独立运行。";
|
||||
self.window.contentView = card;
|
||||
if (!card.image) {
|
||||
NSTextField *fallback = [NSTextField wrappingLabelWithString:card.accessibilityLabel];
|
||||
fallback.frame = NSMakeRect(48, 160, 704, 130);
|
||||
fallback.font = [NSFont systemFontOfSize:24 weight:NSFontWeightSemibold];
|
||||
[card addSubview:fallback];
|
||||
}
|
||||
[self.window center];
|
||||
[self.window makeKeyAndOrderFront:nil];
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
// Only this launcher exits. The FxPlug service has its own process/lifetime.
|
||||
self.exitTimer = [NSTimer scheduledTimerWithTimeInterval:3.0 repeats:NO block:^(NSTimer *timer) {
|
||||
[NSApp terminate:nil];
|
||||
}];
|
||||
// Re-register our own bundle. There is no paid activation or account step.
|
||||
NSURL *bundleURL = NSBundle.mainBundle.bundleURL;
|
||||
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
|
||||
OSStatus status = LSRegisterURL((__bridge CFURLRef)bundleURL, true);
|
||||
if (status != noErr) NSLog(@"LaunchServices registration status: %d", (int)status);
|
||||
});
|
||||
}
|
||||
- (void)openMotion:(id)sender {[self openApplication:@"com.apple.motionapp"];}
|
||||
- (void)openFinalCut:(id)sender {[self openApplication:@"com.apple.FinalCut"];}
|
||||
- (void)openApplication:(NSString *)identifier {
|
||||
NSURL *url=[NSWorkspace.sharedWorkspace URLForApplicationWithBundleIdentifier:identifier];
|
||||
if(url) [NSWorkspace.sharedWorkspace openApplicationAtURL:url configuration:NSWorkspaceOpenConfiguration.configuration completionHandler:nil];
|
||||
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
|
||||
return YES;
|
||||
}
|
||||
@end
|
||||
@@ -17,9 +17,9 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0.6</string>
|
||||
<string>1.2.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>8</string>
|
||||
<string>14</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
|
||||
二进制文件未显示。
|
之后 宽度: | 高度: | 大小: 2.9 MiB |
@@ -0,0 +1 @@
|
||||
{"artifacts":[{"architecture":"arm64","digest":{"sha256":"f01e8c0b7e2676b2961950b0e48efdd2589af218e4b36de7d39f5d1c76852bfb"},"format":"mach-o"}],"captures":[],"environments":[],"providers":[{"id":"ghidra","name":"Ghidra","version":"12.1.4"}],"records":[{"analysis_profile":{"digest":"20565088524a2d0359b9b96f46ac48e679d774c7b0092c56abc06de8fdbec11c","parameters":{"analyzer_preset":"ghidra-default","annotation_policy":"atomic-function-entry-metadata-v1","architecture":"arm64","available_architectures":["arm64"],"compiler_spec_id":"auto-default","decompiler_jump_loads":true,"function_body_evidence":"complete-inclusive-ranges-v1","function_boundary_observations":"ghidra-terminal-call-limitations-v1","function_references":"complete-body-and-entry-reference-manager-v2","import_mode":"ephemeral-source-immutable","instruction_flow_evidence":"decoded-return-pcode-v1","jump_table_evidence":"typed-case-default-blocks-v1","language_id":"auto-from-header","load_image_observations":"source-mappings-entry-context-v2","loader":"auto-from-header","location_resolution":"explicit-address-exact-entry-symbol-first-v3","no_return_repair":"returning-imports-decoded-return-v2","process_launch":"inspected-jvm-launch-support-v1","string_inventory_evidence":"defined-data-coverage-v1","target_format":"mach-o","target_kind":"executable"},"provider":{"id":"ghidra","name":"Ghidra","version":"12.1.4"}},"authority":"shipped-artifact","confidence":"observed","environment":null,"evidence_id":"ev_8bc31392c07788d2482546121297c3f177d527282275ef21fd3e5edef9921544","evidence_links":[],"limitations":["The session serves operations only after Ghidra reports default auto-analysis complete; incomplete analysis does not expose partial results.","The imported Program and temporary project are ephemeral and deleted on close. Annotation operations edit session database metadata; original executable bytes are never written.","Cancelling an active Ghidra request terminates its ephemeral database and discards session annotations. The selected target remains open; the next provider query imports the original artifact again.","Default-space addresses use lowercase 0x-prefixed hexadecimal; other address spaces use <percent-encoded-space>:0x<hex>.","Assembly is Ghidra Listing text and does not claim textual equivalence with Hopper output.","macOS sessions require a matching executable Ghidra native decompiler; REA checks for it but does not build native components or change Gatekeeper quarantine state."],"locations":[],"normalized_result":"0x1000279f4: stp d9, d8, [sp, #-0x30]!\n0x1000279f8: stp x20, x19, [sp, #0x10]\n0x1000279fc: stp x29, x30, [sp, #0x20]\n0x100027a00: add x29, sp, #0x20\n0x100027a04: fmov d8, d0\n0x100027a08: str xzr, [x8, #0x20]\n0x100027a0c: movi v0.2D, #0x0\n0x100027a10: stp q0, q0, [x8]\n0x100027a14: cbz x0, 0x100027bac\n0x100027a18: mov x20, x0\n0x100027a1c: mov x19, x8\n0x100027a20: bl 0x10001a8c0\n0x100027a24: cbz w0, 0x100027bac\n0x100027a28: fmov d1, 0x3ff0000000000000\n0x100027a2c: fcmp d8, d1\n0x100027a30: fcsel d2, d1, d8, gt\n0x100027a34: fcmp d8, #0.0\n0x100027a38: movi d0, #0x0\n0x100027a3c: fcsel d2, d0, d2, mi\n0x100027a40: fcvt s2, d2\n0x100027a44: ldr d3, [x20, #0x80]\n0x100027a48: fcmp d3, d1\n0x100027a4c: fcsel d4, d1, d3, gt\n0x100027a50: fmov d5, -0x4010000000000000\n0x100027a54: fcmp d3, d5\n0x100027a58: fcsel d4, d5, d4, mi\n0x100027a5c: fcmp d4, #0.0\n0x100027a60: cset w8, gt\n0x100027a64: fcvt d3, s2\n0x100027a68: ldp d5, d2, [x20, #0x60]\n0x100027a6c: fcmp d2, d1\n0x100027a70: fcsel d6, d1, d2, gt\n0x100027a74: adrp x9, 0x10005f000\n0x100027a78: ldr d7, [x9, #0xb80]\n0x100027a7c: fmul d6, d6, d7\n0x100027a80: fcmp d2, #0.0\n0x100027a84: fcsel d2, d0, d6, mi\n0x100027a88: fmul d2, d2, d3\n0x100027a8c: fcvt s2, d2\n0x100027a90: add x9, x20, #0x78\n0x100027a94: ldur q6, [x20, #0x88]\n0x100027a98: ld1 v5, x9, \n0x100027a9c: adrp x9, 0x10005f000\n0x100027aa0: ldr q7, [x9, #0x980]\n0x100027aa4: fcmgt v7.2D, v7.2D, v5.2D\n0x100027aa8: fcmlt v16.2D, v6.2D\n0x100027aac: fmov v17.2D, #0x3ff0000000000000\n0x100027ab0: fcmgt v18.2D, v5.2D, v17.2D\n0x100027ab4: bit v5.16B, v17.16B, v18.16B\n0x100027ab8: fcmgt v18.2D, v6.2D, v17.2D\n0x100027abc: bit v6.16B, v17.16B, v18.16B\n0x100027ac0: adrp x9, 0x10005f000\n0x100027ac4: ldr q18, [x9, #0x990]\n0x100027ac8: fmul v5.2D, v5.2D, v18.2D\n0x100027acc: adrp x9, 0x10005f000\n0x100027ad0: ldr q18, [x9, #0x9a0]\n0x100027ad4: fmul v6.2D, v6.2D, v18.2D\n0x100027ad8: adrp x9, 0x10005f000\n0x100027adc: ldr q18, [x9, #0x9b0]\n0x100027ae0: bit v5.16B, v18.16B, v7.16B\n0x100027ae4: bic v6.16B, v6.16B, v16.16B\n0x100027ae8: fmul v6.2D, v6.2D, v3.D[0x0]\n0x100027aec: fmul v5.2D, v5.2D, v3.D[0x0]\n0x100027af0: fcvtn v5.2S, v5.2D\n0x100027af4: fcvtn2 v5.4S, v6.2D\n0x100027af8: ldr d6, [x20, #0x98]\n0x100027afc: fcmp d6, d1\n0x100027b00: fcsel d7, d1, d6, gt\n0x100027b04: adrp x9, 0x10005f000\n0x100027b08: ldr d16, [x9, #0xe68]\n0x100027b0c: fmul d7, d7, d16\n0x100027b10: fcmp d6, #0.0\n0x100027b14:Line truncated
|
||||
@@ -0,0 +1,371 @@
|
||||
Source AIR SHA256: f97705b88f5491057df2117aef457ffca84e11020574cd57742b0670db4484d8
|
||||
Line numbers refer to the complete original metal-objdump text. Static IR; original shader not executed.
|
||||
|
||||
583: define internal fastcc <2 x float> @_ZL25mssFaceShapePinCoordinateDv2_fPU11MTLconstantK16MSSFaceThinPointi18MSSFaceShapeParamsS_(<2 x float> %0, %struct.MSSFaceThinPoint addrspace(2)* nocapture readonly %1, i32 %2, float %3, float %4, float %5, float %6, float %7, float %8, float %9, float %10, float %11, float %12, <2 x float> %13) unnamed_addr #2 {
|
||||
584: %15 = alloca [17 x <3 x i32>], align 16
|
||||
585: %16 = alloca [16 x <2 x i32>], align 8
|
||||
586: %17 = alloca [4 x <2 x i32>], align 8
|
||||
587: %18 = alloca [6 x <3 x i32>], align 16
|
||||
588: %19 = alloca [6 x <2 x i32>], align 8
|
||||
589: %20 = alloca [3 x <2 x i32>], align 8
|
||||
590: %21 = alloca [11 x i32], align 4
|
||||
591: %22 = alloca [11 x <2 x float>], align 8
|
||||
592: %23 = add nsw i32 %2, 75
|
||||
593: %24 = sext i32 %23 to i64
|
||||
594: %25 = getelementptr inbounds %struct.MSSFaceThinPoint, %struct.MSSFaceThinPoint addrspace(2)* %1, i64 %24, i32 0
|
||||
595: %26 = load <2 x float>, <2 x float> addrspace(2)* %25, align 8, !tbaa !62
|
||||
596: %27 = add nsw i32 %2, 59
|
||||
597: %28 = sext i32 %27 to i64
|
||||
598: %29 = getelementptr inbounds %struct.MSSFaceThinPoint, %struct.MSSFaceThinPoint addrspace(2)* %1, i64 %28, i32 0
|
||||
599: %30 = load <2 x float>, <2 x float> addrspace(2)* %29, align 8, !tbaa !62
|
||||
600: %31 = add nsw i32 %2, 67
|
||||
601: %32 = sext i32 %31 to i64
|
||||
602: %33 = getelementptr inbounds %struct.MSSFaceThinPoint, %struct.MSSFaceThinPoint addrspace(2)* %1, i64 %32, i32 0
|
||||
603: %34 = load <2 x float>, <2 x float> addrspace(2)* %33, align 8, !tbaa !62
|
||||
604: %35 = add nsw i32 %2, 74
|
||||
605: %36 = sext i32 %35 to i64
|
||||
606: %37 = getelementptr inbounds %struct.MSSFaceThinPoint, %struct.MSSFaceThinPoint addrspace(2)* %1, i64 %36, i32 0
|
||||
607: %38 = load <2 x float>, <2 x float> addrspace(2)* %37, align 8, !tbaa !62
|
||||
608: %39 = add nsw i32 %2, 60
|
||||
609: %40 = sext i32 %39 to i64
|
||||
610: %41 = getelementptr inbounds %struct.MSSFaceThinPoint, %struct.MSSFaceThinPoint addrspace(2)* %1, i64 %40, i32 0
|
||||
611: %42 = load <2 x float>, <2 x float> addrspace(2)* %41, align 8, !tbaa !62
|
||||
612: %43 = fadd fast <2 x float> %42, %38
|
||||
613: %44 = fmul fast <2 x float> %43, <float 5.000000e-01, float 5.000000e-01>
|
||||
614: %45 = fsub fast <2 x float> %26, %30
|
||||
615: %46 = fmul fast <2 x float> %45, %13
|
||||
616: %47 = tail call fast float @air.dot.v2f32(<2 x float> %46, <2 x float> %46) #3
|
||||
617: %48 = tail call fast float @air.fast_sqrt.f32(float %47) #3
|
||||
618: %49 = fsub fast <2 x float> %34, %44
|
||||
619: %50 = fmul fast <2 x float> %49, %13
|
||||
620: %51 = tail call fast float @air.dot.v2f32(<2 x float> %50, <2 x float> %50) #3
|
||||
621: %52 = tail call fast float @air.fast_sqrt.f32(float %51) #3
|
||||
622: %53 = fmul fast float %52, 2.000000e+00
|
||||
623: %54 = tail call fast float @air.fast_fmax.f32(float %48, float %53) #3
|
||||
624: %55 = tail call fast float @air.fast_fmax.f32(float %54, float 1.000000e+00) #3
|
||||
625: %56 = fdiv fast float 2.800000e+02, %55
|
||||
626: %57 = tail call fast float @air.fast_clamp.f32(float %56, float 1.000000e+00, float 2.250000e+00) #3
|
||||
627: %58 = fmul fast float %57, %3
|
||||
628: %59 = fmul fast float %57, %4
|
||||
629: %60 = fmul fast float %57, %5
|
||||
630: %61 = fmul fast float %57, %6
|
||||
631: %62 = fmul fast float %57, %7
|
||||
632: %63 = fmul fast float %57, %8
|
||||
633: %64 = fmul fast float %57, %9
|
||||
634: %65 = tail call fast float @air.fast_fabs.f32(float %58) #3
|
||||
635: %66 = fcmp fast ogt float %65, 0x3EB0C6F7A0000000
|
||||
|
||||
899: 251: ; preds = %212, %200
|
||||
900: %252 = phi <2 x float> [ %248, %212 ], [ %201, %200 ]
|
||||
901: %253 = tail call fast float @air.fast_fabs.f32(float %61) #3
|
||||
902: %254 = fcmp fast ogt float %253, 0x3EB0C6F7A0000000
|
||||
903: br i1 %254, label %255, label %309
|
||||
904:
|
||||
905: 255: ; preds = %251
|
||||
906: %256 = bitcast [6 x <3 x i32>]* %18 to i8*
|
||||
907: call void @llvm.lifetime.start.p0i8(i64 96, i8* nonnull %256) #8
|
||||
908: %257 = getelementptr inbounds [6 x <3 x i32>], [6 x <3 x i32>]* %18, i64 0, i64 0
|
||||
909: store <3 x i32> <i32 72, i32 71, i32 29>, <3 x i32>* %257, align 16
|
||||
910: %258 = getelementptr inbounds [6 x <3 x i32>], [6 x <3 x i32>]* %18, i64 0, i64 1
|
||||
911: store <3 x i32> <i32 62, i32 63, i32 29>, <3 x i32>* %258, align 16
|
||||
912: %259 = getelementptr inbounds [6 x <3 x i32>], [6 x <3 x i32>]* %18, i64 0, i64 2
|
||||
913: store <3 x i32> <i32 71, i32 70, i32 29>, <3 x i32>* %259, align 16
|
||||
914: %260 = getelementptr inbounds [6 x <3 x i32>], [6 x <3 x i32>]* %18, i64 0, i64 3
|
||||
915: store <3 x i32> <i32 63, i32 64, i32 29>, <3 x i32>* %260, align 16
|
||||
916: %261 = getelementptr inbounds [6 x <3 x i32>], [6 x <3 x i32>]* %18, i64 0, i64 4
|
||||
917: store <3 x i32> <i32 70, i32 71, i32 29>, <3 x i32>* %261, align 16
|
||||
918: %262 = getelementptr inbounds [6 x <3 x i32>], [6 x <3 x i32>]* %18, i64 0, i64 5
|
||||
919: store <3 x i32> <i32 64, i32 63, i32 29>, <3 x i32>* %262, align 16
|
||||
920: %263 = insertelement <2 x float> undef, float %61, i64 0
|
||||
921: %264 = shufflevector <2 x float> %263, <2 x float> undef, <2 x i32> zeroinitializer
|
||||
922: br label %266
|
||||
923:
|
||||
924: 265: ; preds = %305
|
||||
925: call void @llvm.lifetime.end.p0i8(i64 96, i8* nonnull %256) #8
|
||||
926: br label %309
|
||||
927:
|
||||
928: 266: ; preds = %305, %255
|
||||
929: %267 = phi <2 x float> [ %252, %255 ], [ %306, %305 ]
|
||||
930: %268 = phi i32 [ 0, %255 ], [ %307, %305 ]
|
||||
931: %269 = zext i32 %268 to i64
|
||||
932: %270 = getelementptr inbounds [6 x <3 x i32>], [6 x <3 x i32>]* %18, i64 0, i64 %269
|
||||
933: %271 = load <3 x i32>, <3 x i32>* %270, align 16
|
||||
934: %272 = extractelement <3 x i32> %271, i64 0
|
||||
935: %273 = add nsw i32 %272, %2
|
||||
936: %274 = sext i32 %273 to i64
|
||||
937: %275 = getelementptr inbounds %struct.MSSFaceThinPoint, %struct.MSSFaceThinPoint addrspace(2)* %1, i64 %274, i32 0
|
||||
938: %276 = load <2 x float>, <2 x float> addrspace(2)* %275, align 8, !tbaa !62
|
||||
939: %277 = extractelement <3 x i32> %271, i64 1
|
||||
940: %278 = add nsw i32 %277, %2
|
||||
941: %279 = sext i32 %278 to i64
|
||||
942: %280 = getelementptr inbounds %struct.MSSFaceThinPoint, %struct.MSSFaceThinPoint addrspace(2)* %1, i64 %279, i32 0
|
||||
943: %281 = load <2 x float>, <2 x float> addrspace(2)* %280, align 8, !tbaa !62
|
||||
944: %282 = fsub fast <2 x float> %276, %281
|
||||
945: %283 = tail call fast float @air.dot.v2f32(<2 x float> %282, <2 x float> %282) #3
|
||||
946: %284 = tail call fast float @air.fast_sqrt.f32(float %283) #3
|
||||
947: %285 = fcmp fast ugt float %284, 0x3F1A36E2E0000000
|
||||
948: br i1 %285, label %286, label %305
|
||||
949:
|
||||
950: 286: ; preds = %266
|
||||
951: %287 = extractelement <3 x i32> %271, i64 2
|
||||
952: %288 = add nsw i32 %287, %2
|
||||
953: %289 = sext i32 %288 to i64
|
||||
954: %290 = getelementptr inbounds %struct.MSSFaceThinPoint, %struct.MSSFaceThinPoint addrspace(2)* %1, i64 %289, i32 0
|
||||
955: %291 = load <2 x float>, <2 x float> addrspace(2)* %290, align 8, !tbaa !62
|
||||
956: %292 = fsub fast <2 x float> %291, %276
|
||||
957: %293 = fsub fast <2 x float> %267, %276
|
||||
958: %294 = tail call fast float @air.dot.v2f32(<2 x float> %293, <2 x float> %293) #3
|
||||
959: %295 = tail call fast float @air.fast_sqrt.f32(float %294) #3
|
||||
960: %296 = fdiv fast float %295, %284
|
||||
961: %297 = fsub fast float 1.000000e+00, %296
|
||||
962: %298 = tail call fast float @air.fast_clamp.f32(float %297, float 0.000000e+00, float 1.000000e+00) #3
|
||||
963: %299 = insertelement <2 x float> undef, float %298, i64 0
|
||||
964: %300 = shufflevector <2 x float> %299, <2 x float> undef, <2 x i32> zeroinitializer
|
||||
965: %301 = fmul fast <2 x float> %292, %264
|
||||
966: %302 = fmul fast <2 x float> %301, %300
|
||||
967: %303 = fsub fast <2 x float> %267, %302
|
||||
968: %304 = tail call fast <2 x float> @air.fast_clamp.v2f32(<2 x float> %303, <2 x float> zeroinitializer, <2 x float> <float 1.000000e+00, float 1.000000e+00>) #3
|
||||
969: br label %305
|
||||
970:
|
||||
971: 305: ; preds = %286, %266
|
||||
972: %306 = phi <2 x float> [ %304, %286 ], [ %267, %266 ]
|
||||
973: %307 = add nuw nsw i32 %268, 1
|
||||
974: %308 = icmp eq i32 %307, 6
|
||||
975: br i1 %308, label %265, label %266, !llvm.loop !67
|
||||
976:
|
||||
977: 309: ; preds = %265, %251
|
||||
978: %310 = phi <2 x float> [ %306, %265 ], [ %252, %251 ]
|
||||
979: %311 = tail call fast float @air.fast_fabs.f32(float %62) #3
|
||||
980: %312 = fcmp fast ogt float %311, 0x3EB0C6F7A0000000
|
||||
981: br i1 %312, label %313, label %372
|
||||
982:
|
||||
983: 313: ; preds = %309
|
||||
984: %314 = bitcast [6 x <2 x i32>]* %19 to i8*
|
||||
985: call void @llvm.lifetime.start.p0i8(i64 48, i8* nonnull %314) #8
|
||||
986: %315 = getelementptr inbounds [6 x <2 x i32>], [6 x <2 x i32>]* %19, i64 0, i64 0
|
||||
|
||||
1127: 422: ; preds = %383, %372
|
||||
1128: %423 = phi <2 x float> [ %419, %383 ], [ %373, %372 ]
|
||||
1129: %424 = tail call fast float @air.fast_fabs.f32(float %64) #3
|
||||
1130: %425 = fcmp fast ogt float %424, 0x3EB0C6F7A0000000
|
||||
1131: br i1 %425, label %426, label %585
|
||||
1132:
|
||||
1133: 426: ; preds = %422
|
||||
1134: %427 = bitcast [11 x i32]* %21 to i8*
|
||||
1135: call void @llvm.lifetime.start.p0i8(i64 44, i8* nonnull %427) #8
|
||||
1136: %428 = getelementptr inbounds [11 x i32], [11 x i32]* %21, i64 0, i64 0
|
||||
1137: store i32 62, i32* %428, align 4
|
||||
1138: %429 = getelementptr inbounds [11 x i32], [11 x i32]* %21, i64 0, i64 1
|
||||
1139: store i32 63, i32* %429, align 4
|
||||
1140: %430 = getelementptr inbounds [11 x i32], [11 x i32]* %21, i64 0, i64 2
|
||||
1141: store i32 64, i32* %430, align 4
|
||||
1142: %431 = getelementptr inbounds [11 x i32], [11 x i32]* %21, i64 0, i64 3
|
||||
1143: store i32 65, i32* %431, align 4
|
||||
1144: %432 = getelementptr inbounds [11 x i32], [11 x i32]* %21, i64 0, i64 4
|
||||
1145: store i32 66, i32* %432, align 4
|
||||
1146: %433 = getelementptr inbounds [11 x i32], [11 x i32]* %21, i64 0, i64 5
|
||||
1147: store i32 67, i32* %433, align 4
|
||||
1148: %434 = getelementptr inbounds [11 x i32], [11 x i32]* %21, i64 0, i64 6
|
||||
1149: store i32 68, i32* %434, align 4
|
||||
1150: %435 = getelementptr inbounds [11 x i32], [11 x i32]* %21, i64 0, i64 7
|
||||
1151: store i32 69, i32* %435, align 4
|
||||
1152: %436 = getelementptr inbounds [11 x i32], [11 x i32]* %21, i64 0, i64 8
|
||||
1153: store i32 70, i32* %436, align 4
|
||||
1154: %437 = getelementptr inbounds [11 x i32], [11 x i32]* %21, i64 0, i64 9
|
||||
1155: store i32 71, i32* %437, align 4
|
||||
1156: %438 = getelementptr inbounds [11 x i32], [11 x i32]* %21, i64 0, i64 10
|
||||
1157: store i32 72, i32* %438, align 4
|
||||
1158: %439 = add nsw i32 %2, 46
|
||||
1159: %440 = sext i32 %439 to i64
|
||||
1160: %441 = getelementptr inbounds %struct.MSSFaceThinPoint, %struct.MSSFaceThinPoint addrspace(2)* %1, i64 %440, i32 0
|
||||
1161: %442 = load <2 x float>, <2 x float> addrspace(2)* %441, align 8, !tbaa !62
|
||||
1162: %443 = add nsw i32 %2, 48
|
||||
1163: %444 = sext i32 %443 to i64
|
||||
1164: %445 = getelementptr inbounds %struct.MSSFaceThinPoint, %struct.MSSFaceThinPoint addrspace(2)* %1, i64 %444, i32 0
|
||||
1165: %446 = load <2 x float>, <2 x float> addrspace(2)* %445, align 8, !tbaa !62
|
||||
1166: %447 = bitcast [11 x <2 x float>]* %22 to i8*
|
||||
1167: call void @llvm.lifetime.start.p0i8(i64 88, i8* nonnull %447) #8
|
||||
1168: br label %492
|
||||
1169:
|
||||
1170: 448: ; preds = %492
|
||||
1171: %449 = fsub fast <2 x float> %442, %446
|
||||
1172: %450 = extractelement <2 x float> %423, i64 1
|
||||
1173: %451 = extractelement <2 x float> %423, i64 0
|
||||
1174: %452 = getelementptr inbounds [11 x <2 x float>], [11 x <2 x float>]* %22, i64 0, i64 10
|
||||
1175: %453 = load <2 x float>, <2 x float>* %452, align 8, !tbaa !62
|
||||
1176: br label %454
|
||||
1177:
|
||||
1178: 454: ; preds = %484, %448
|
||||
1179: %455 = phi <2 x float> [ %453, %448 ], [ %460, %484 ]
|
||||
1180: %456 = phi i8 [ 0, %448 ], [ %485, %484 ]
|
||||
1181: %457 = phi i32 [ 0, %448 ], [ %486, %484 ]
|
||||
1182: %458 = zext i32 %457 to i64
|
||||
1183: %459 = getelementptr inbounds [11 x <2 x float>], [11 x <2 x float>]* %22, i64 0, i64 %458
|
||||
1184: %460 = load <2 x float>, <2 x float>* %459, align 8, !tbaa !62
|
||||
1185: %461 = extractelement <2 x float> %455, i64 1
|
||||
1186: %462 = extractelement <2 x float> %460, i64 1
|
||||
1187: %463 = fcmp fast ule float %462, %450
|
||||
1188: %464 = fcmp fast ogt float %461, %450
|
||||
1189: %465 = xor i1 %463, %464
|
||||
1190: br i1 %465, label %484, label %466
|
||||
1191:
|
||||
1192: 466: ; preds = %454
|
||||
1193: %467 = fsub fast float %461, %462
|
||||
1194: %468 = tail call fast float @air.fast_fabs.f32(float %467) #3
|
||||
1195: %469 = fcmp fast ugt float %468, 0x3EB0C6F7A0000000
|
||||
1196: %470 = fcmp fast olt float %467, 0.000000e+00
|
||||
1197: %471 = select fast i1 %470, float 0xBEB0C6F7A0000000, float 0x3EB0C6F7A0000000
|
||||
1198: %472 = select i1 %469, float %467, float %471
|
||||
1199: %473 = extractelement <2 x float> %455, i64 0
|
||||
1200: %474 = extractelement <2 x float> %460, i64 0
|
||||
1201: %475 = fsub fast float %473, %474
|
||||
1202: %476 = fsub fast float %450, %462
|
||||
1203: %477 = fmul fast float %475, %476
|
||||
1204: %478 = fdiv fast float %477, %472
|
||||
1205: %479 = fadd fast float %478, %474
|
||||
1206: %480 = fcmp fast olt float %451, %479
|
||||
1207: br i1 %480, label %481, label %484
|
||||
1208:
|
||||
1209: 481: ; preds = %466
|
||||
1210: %482 = and i8 %456, 1
|
||||
1211: %483 = xor i8 %482, 1
|
||||
1212: br label %484
|
||||
1213:
|
||||
1214: 484: ; preds = %481, %466, %454
|
||||
1215: %485 = phi i8 [ %483, %481 ], [ %456, %466 ], [ %456, %454 ]
|
||||
1216: %486 = add nuw nsw i32 %457, 1
|
||||
1217: %487 = icmp eq i32 %486, 11
|
||||
1218: br i1 %487, label %488, label %454, !llvm.loop !70
|
||||
1219:
|
||||
1220: 488: ; preds = %484
|
||||
1221: %489 = getelementptr inbounds [11 x <2 x float>], [11 x <2 x float>]* %22, i64 0, i64 0
|
||||
1222: %490 = and i8 %485, 1
|
||||
1223: %491 = load <2 x float>, <2 x float>* %489, align 8, !tbaa !62
|
||||
1224: br label %506
|
||||
1225:
|
||||
1226: 492: ; preds = %492, %426
|
||||
1227: %493 = phi i32 [ 0, %426 ], [ %502, %492 ]
|
||||
1228: %494 = zext i32 %493 to i64
|
||||
1229: %495 = getelementptr inbounds [11 x i32], [11 x i32]* %21, i64 0, i64 %494
|
||||
1230: %496 = load i32, i32* %495, align 4, !tbaa !53
|
||||
1231: %497 = add nsw i32 %496, %2
|
||||
1232: %498 = sext i32 %497 to i64
|
||||
1233: %499 = getelementptr inbounds %struct.MSSFaceThinPoint, %struct.MSSFaceThinPoint addrspace(2)* %1, i64 %498, i32 0
|
||||
1234: %500 = load <2 x float>, <2 x float> addrspace(2)* %499, align 8, !tbaa !62
|
||||
1235: %501 = getelementptr inbounds [11 x <2 x float>], [11 x <2 x float>]* %22, i64 0, i64 %494
|
||||
1236: store <2 x float> %500, <2 x float>* %501, align 8, !tbaa !62
|
||||
1237: %502 = add nuw nsw i32 %493, 1
|
||||
1238: %503 = icmp eq i32 %502, 11
|
||||
1239: br i1 %503, label %448, label %492, !llvm.loop !71
|
||||
1240:
|
||||
1241: 504: ; preds = %531
|
||||
1242: %505 = icmp eq i8 %490, 0
|
||||
1243: br i1 %505, label %559, label %540
|
||||
1244:
|
||||
1245: 506: ; preds = %531, %488
|
||||
1246: %507 = phi i32 [ 0, %488 ], [ %513, %531 ]
|
||||
1247: %508 = phi <2 x float> [ %491, %488 ], [ %538, %531 ]
|
||||
1248: %509 = phi float [ 1.000000e+01, %488 ], [ %537, %531 ]
|
||||
1249: %510 = zext i32 %507 to i64
|
||||
1250: %511 = getelementptr inbounds [11 x <2 x float>], [11 x <2 x float>]* %22, i64 0, i64 %510
|
||||
1251: %512 = load <2 x float>, <2 x float>* %511, align 8, !tbaa !62
|
||||
1252: %513 = add nuw nsw i32 %507, 1
|
||||
1253: %514 = trunc i32 %513 to i8
|
||||
1254: %515 = urem i8 %514, 11
|
||||
1255: %516 = zext i8 %515 to i64
|
||||
1256: %517 = getelementptr inbounds [11 x <2 x float>], [11 x <2 x float>]* %22, i64 0, i64 %516
|
||||
1257: %518 = load <2 x float>, <2 x float>* %517, align 8, !tbaa !62
|
||||
1258: %519 = fsub fast <2 x float> %518, %512
|
||||
1259: %520 = tail call fast float @air.dot.v2f32(<2 x float> %519, <2 x float> %519) #3
|
||||
1260: %521 = fcmp fast ugt float %520, 0x3EB0C6F7A0000000
|
||||
1261: br i1 %521, label %522, label %531
|
||||
1262:
|
||||
1263: 522: ; preds = %506
|
||||
1264: %523 = fsub fast <2 x float> %423, %512
|
||||
1265: %524 = tail call fast float @air.dot.v2f32(<2 x float> %523, <2 x float> %519) #3
|
||||
1266: %525 = fdiv fast float %524, %520
|
||||
1267: %526 = tail call fast float @air.fast_clamp.f32(float %525, float 0.000000e+00, float 1.000000e+00) #3
|
||||
1268: %527 = insertelement <2 x float> undef, float %526, i64 0
|
||||
1269: %528 = shufflevector <2 x float> %527, <2 x float> undef, <2 x i32> zeroinitializer
|
||||
1270: %529 = fmul fast <2 x float> %528, %519
|
||||
1271: %530 = fadd fast <2 x float> %529, %512
|
||||
1272: br label %531
|
||||
1273:
|
||||
1274: 531: ; preds = %522, %506
|
||||
1275: %532 = phi <2 x float> [ %530, %522 ], [ %512, %506 ]
|
||||
1276: %533 = fsub fast <2 x float> %423, %532
|
||||
1277: %534 = tail call fast float @air.dot.v2f32(<2 x float> %533, <2 x float> %533) #3
|
||||
1278: %535 = tail call fast float @air.fast_sqrt.f32(float %534) #3
|
||||
1279: %536 = fcmp fast olt float %535, %509
|
||||
1280: %537 = select i1 %536, float %535, float %509
|
||||
1281: %538 = select i1 %536, <2 x float> %532, <2 x float> %508
|
||||
1282: %539 = icmp eq i32 %513, 11
|
||||
1283: br i1 %539, label %504, label %506, !llvm.loop !72
|
||||
1284:
|
||||
1285: 540: ; preds = %504
|
||||
1286: %541 = fsub fast <2 x float> <float -0.000000e+00, float -0.000000e+00>, %449
|
||||
1287: %542 = tail call fast float @air.dot.v2f32(<2 x float> %541, <2 x float> %541) #3
|
||||
1288: %543 = tail call fast float @air.fast_sqrt.f32(float %542) #3
|
||||
1289: %544 = fcmp fast ugt float %543, 0x3F1A36E2E0000000
|
||||
1290: br i1 %544, label %545, label %583
|
||||
1291:
|
||||
1292: 545: ; preds = %540
|
||||
1293: %546 = tail call fast float @air.dot.v2f32(<2 x float> zeroinitializer, <2 x float> zeroinitializer) #3
|
||||
1294: %547 = tail call fast float @air.fast_sqrt.f32(float %546) #3
|
||||
1295: %548 = fdiv fast float %547, %543
|
||||
1296: %549 = fsub fast float 1.000000e+00, %548
|
||||
1297: %550 = tail call fast float @air.fast_clamp.f32(float %549, float 0.000000e+00, float 1.000000e+00) #3
|
||||
1298: %551 = insertelement <2 x float> undef, float %550, i64 0
|
||||
1299: %552 = shufflevector <2 x float> %551, <2 x float> undef, <2 x i32> zeroinitializer
|
||||
1300: %553 = insertelement <2 x float> undef, float %64, i64 0
|
||||
1301: %554 = shufflevector <2 x float> %553, <2 x float> undef, <2 x i32> zeroinitializer
|
||||
1302: %555 = fmul fast <2 x float> %449, %554
|
||||
1303: %556 = fmul fast <2 x float> %555, %552
|
||||
1304: %557 = fsub fast <2 x float> %423, %556
|
||||
1305: %558 = tail call fast <2 x float> @air.fast_clamp.v2f32(<2 x float> %557, <2 x float> zeroinitializer, <2 x float> <float 1.000000e+00, float 1.000000e+00>) #3
|
||||
1306: br label %583
|
||||
1307:
|
||||
1308: 559: ; preds = %504
|
||||
1309: %560 = tail call fast float @air.dot.v2f32(<2 x float> %449, <2 x float> %449) #3
|
||||
1310: %561 = tail call fast float @air.fast_sqrt.f32(float %560) #3
|
||||
1311: %562 = fcmp fast olt float %537, %561
|
||||
1312: br i1 %562, label %563, label %583
|
||||
1313:
|
||||
1314: 563: ; preds = %559
|
||||
1315: %564 = fsub fast <2 x float> <float -0.000000e+00, float -0.000000e+00>, %449
|
||||
1316: %565 = tail call fast float @air.dot.v2f32(<2 x float> %564, <2 x float> %564) #3
|
||||
1317: %566 = tail call fast float @air.fast_sqrt.f32(float %565) #3
|
||||
1318: %567 = fcmp fast ugt float %566, 0x3F1A36E2E0000000
|
||||
1319: br i1 %567, label %568, label %583
|
||||
1320:
|
||||
1321: 568: ; preds = %563
|
||||
1322: %569 = fsub fast <2 x float> %423, %538
|
||||
1323: %570 = tail call fast float @air.dot.v2f32(<2 x float> %569, <2 x float> %569) #3
|
||||
1324: %571 = tail call fast float @air.fast_sqrt.f32(float %570) #3
|
||||
1325: %572 = fdiv fast float %571, %566
|
||||
1326: %573 = fsub fast float 1.000000e+00, %572
|
||||
1327: %574 = tail call fast float @air.fast_clamp.f32(float %573, float 0.000000e+00, float 1.000000e+00) #3
|
||||
1328: %575 = insertelement <2 x float> undef, float %574, i64 0
|
||||
1329: %576 = shufflevector <2 x float> %575, <2 x float> undef, <2 x i32> zeroinitializer
|
||||
1330: %577 = insertelement <2 x float> undef, float %64, i64 0
|
||||
1331: %578 = shufflevector <2 x float> %577, <2 x float> undef, <2 x i32> zeroinitializer
|
||||
1332: %579 = fmul fast <2 x float> %449, %578
|
||||
1333: %580 = fmul fast <2 x float> %579, %576
|
||||
1334: %581 = fsub fast <2 x float> %423, %580
|
||||
1335: %582 = tail call fast <2 x float> @air.fast_clamp.v2f32(<2 x float> %581, <2 x float> zeroinitializer, <2 x float> <float 1.000000e+00, float 1.000000e+00>) #3
|
||||
1336: br label %583
|
||||
1337:
|
||||
1338: 583: ; preds = %568, %563, %559, %545, %540
|
||||
1339: %584 = phi <2 x float> [ %423, %559 ], [ %558, %545 ], [ %423, %540 ], [ %582, %568 ], [ %423, %563 ]
|
||||
1340: call void @llvm.lifetime.end.p0i8(i64 88, i8* nonnull %447) #8
|
||||
1341: call void @llvm.lifetime.end.p0i8(i64 44, i8* nonnull %427) #8
|
||||
1342: br label %585
|
||||
1343:
|
||||
1344: 585: ; preds = %583, %422
|
||||
1345: %586 = phi <2 x float> [ %584, %583 ], [ %423, %422 ]
|
||||
1346: %587 = tail call fast float @air.fast_fabs.f32(float %10) #3
|
||||
1347: %588 = fcmp fast ogt float %587, 0x3EB0C6F7A0000000
|
||||
1348: br i1 %588, label %589, label %613
|
||||
|
||||
4634: !27 = !{i32 8, !"air.buffer", !"air.location_index", i32 7, i32 1, !"air.read", !"air.struct_type_info", !28, !"air.arg_type_size", i32 40, !"air.arg_type_align_size", i32 4, !"air.arg_type_name", !"MSSFaceShapeParams", !"air.arg_name", !"faceShapeParams"}
|
||||
4635: !28 = !{i32 0, i32 4, i32 0, !"float", !"shoulian", i32 4, i32 4, i32 0, !"float", !"zhailian", i32 8, i32 4, i32 0, !"float", !"vlian", i32 12, i32 4, i32 0, !"float", !"xiaegu", i32 16, i32 4, i32 0, !"float", !"quangu", i32 20, i32 4, i32 0, !"float", !"xiaba", i32 24, i32 4, i32 0, !"float", !"duanlian", i32 28, i32 4, i32 0, !"float", !"xiaotou", i32 32, i32 4, i32 0, !"float", !"xiaotoufanwei", i32 36, i32 4, i32 0, !"float", !"dayan"}
|
||||
@@ -0,0 +1,178 @@
|
||||
{
|
||||
"authority": "Static shipped-artifact analysis; original shader not executed. Public Vision76 region mapping reused from slim-pose-report.json.",
|
||||
"target_arm64_sha256": "f01e8c0b7e2676b2961950b0e48efdd2589af218e4b36de7d39f5d1c76852bfb",
|
||||
"air_sha256": "f97705b88f5491057df2117aef457ffca84e11020574cd57742b0670db4484d8",
|
||||
"evidence_ids": {
|
||||
"cpu_constants": "ev_d1b8a49fc62e384710bac234967c3e8cbf7c5127081c9b2c6f6a0bbd80b80967",
|
||||
"cpu_assembly": "ev_8bc31392c07788d2482546121297c3f177d527282275ef21fd3e5edef9921544"
|
||||
},
|
||||
"common": {
|
||||
"uv": "full-image normalized, y down; production stores y up and must convert points and q together",
|
||||
"gain": "clamp(280/max(length((P75-P59)*pixelSize),2*length((P67-(P74+P60)/2)*pixelSize),1),1,2.25)",
|
||||
"original_order": [
|
||||
"thin17",
|
||||
"narrow16",
|
||||
"vFace4",
|
||||
"jaw6",
|
||||
"cheek6",
|
||||
"chin3",
|
||||
"shortPolygon",
|
||||
"smallHead",
|
||||
"bigEye"
|
||||
],
|
||||
"enable_threshold": "abs(effectiveRatio)>1e-6",
|
||||
"per_pin_or_field_result": "clamp(q-newOffset,0,1)"
|
||||
},
|
||||
"jaw": {
|
||||
"struct_field": "xiaegu offset12 / helper parameter6",
|
||||
"ui_range": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"cpu_scale": 0.05,
|
||||
"constant_address": "0x10005f9a8",
|
||||
"pins_origin_radiusNeighbour_directionAnchor": [
|
||||
[
|
||||
72,
|
||||
71,
|
||||
29
|
||||
],
|
||||
[
|
||||
62,
|
||||
63,
|
||||
29
|
||||
],
|
||||
[
|
||||
71,
|
||||
70,
|
||||
29
|
||||
],
|
||||
[
|
||||
63,
|
||||
64,
|
||||
29
|
||||
],
|
||||
[
|
||||
70,
|
||||
71,
|
||||
29
|
||||
],
|
||||
[
|
||||
64,
|
||||
63,
|
||||
29
|
||||
]
|
||||
],
|
||||
"radius": "length(Porigin-Pneighbour) in UV; skip radius<=.0001",
|
||||
"direction": "P29-Porigin",
|
||||
"weight": "clamp(1-length(q-Porigin)/radius,0,1)",
|
||||
"step": "q=clamp(q-direction*clamp(jaw,0,1)*.05*gain*weight,0,1)",
|
||||
"execution": "6 sequential q updates; each radius uses the second triplet index, unlike thin/chin which use the direction anchor distance",
|
||||
"air_lines": [
|
||||
899,
|
||||
986
|
||||
]
|
||||
},
|
||||
"short": {
|
||||
"struct_field": "duanlian offset24 / helper parameter9",
|
||||
"ui_range": [
|
||||
-1,
|
||||
1
|
||||
],
|
||||
"cpu_scale": 0.25,
|
||||
"constant_address": "0x10005f9e0",
|
||||
"cpu_clamp_min_address": "0x10005f9d0 = -1",
|
||||
"cpu_scaled_min_address": "0x10005f9f0 = -.25",
|
||||
"polygon_indices": [
|
||||
62,
|
||||
63,
|
||||
64,
|
||||
65,
|
||||
66,
|
||||
67,
|
||||
68,
|
||||
69,
|
||||
70,
|
||||
71,
|
||||
72
|
||||
],
|
||||
"polygon_closure": "P72->P62",
|
||||
"axis": "P46-P48 (nose crest region)",
|
||||
"radius": "length(axis) in UV; skip radius<=.0001",
|
||||
"inside": "even-odd point-in-polygon; weight1",
|
||||
"outside": "d=minimum distance to11 closed segments; weight=clamp(1-d/radius,0,1), only when d<radius",
|
||||
"step": "q=clamp(q-axis*clamp(short,-1,1)*.25*gain*weight,0,1)",
|
||||
"nearest_segment_threshold": "squared edge length>1e-6 then project t=clamp(dot(q-A,B-A)/dot(B-A,B-A),0,1); else choose A",
|
||||
"ray_crossing_denominator": "dy if abs(dy)>1e-6 else signed1e-6",
|
||||
"air_lines": [
|
||||
1127,
|
||||
1340
|
||||
]
|
||||
},
|
||||
"implementation": {
|
||||
"source": "beatygril/beatygril/Plugin/PGFaceProcessor.m",
|
||||
"order": [
|
||||
"thin17",
|
||||
"existing analytic narrow/V/cheek/small-head body",
|
||||
"jaw6",
|
||||
"chin3",
|
||||
"shortPolygon",
|
||||
"existing analytic bigEye"
|
||||
],
|
||||
"control_coefficients": {
|
||||
"jaw": 0.05,
|
||||
"short": 0.25
|
||||
},
|
||||
"strength": "Original CPU clamps a common shape strength. Product fixes that multiplier at1 and maps it to no user parameter: PGAmount/s.amount is smoothing strength only and is not used by thin/chin/jaw/short. All4 share the same pixel-size gain. Each shape uses its own control; PGMix is applied independently to the total output later by the host adapter. No new common shape-strength UI is introduced.",
|
||||
"sampling": "All sequential coordinates are evaluated within one CIWarpKernel per face; image and all4 masks share the same method.",
|
||||
"missing_or_invalid76": "Preserve legacy face/archive and other5 analytic controls; skip thin/chin/jaw/short exactly.",
|
||||
"unchanged": "Existing thin17/chin3 index order, constants and formulas remain unchanged.",
|
||||
"source_sha256": "e4122ca95f7ba41bc925b4cc4bf2bf88a72044ea41880cd66acd9c8278a7daf4"
|
||||
},
|
||||
"limits": [
|
||||
"No original target app/shader executed; these are static CPU/AIR observations.",
|
||||
"The complete vendor chain is not reconstructed: narrow/V/cheek/small-head/eye algorithms and their ordering remain project approximations. Isolated jaw/short plus recovered thin/chin ordering is tested.",
|
||||
"No new explicit yaw, pitch, roll or landmark-quality attenuation is inferred. Profile behavior follows detected 2D landmarks, as in the recovered branches.",
|
||||
"Physical PNG checks bypass the host IOSurface adapter. Original vendor pixel equivalence and Motion/FCP1.1.2 runtime acceptance remain unverified.",
|
||||
"Independent public Vision CGImage and CIImage detections differ slightly. Tests allow .025 normalized location difference; eye/temple displacement tests measure the actual GPU inverse field."
|
||||
],
|
||||
"verification": {
|
||||
"core": {
|
||||
"exit_code": 0,
|
||||
"beauty_checks": 49,
|
||||
"face_checks": 116,
|
||||
"slim_checks": 796,
|
||||
"total": 961
|
||||
},
|
||||
"real_frames_asan": {
|
||||
"exit_code": 0,
|
||||
"checks": 886,
|
||||
"frames": [
|
||||
"public NASA astronaut",
|
||||
"local user upright/rolled0s",
|
||||
"local user profile4s"
|
||||
],
|
||||
"diagnostics": []
|
||||
},
|
||||
"max_uv_oracle_error": 1.09828022e-05,
|
||||
"physical_checks": "Public Vision76 lower-contour response, pupil/upper-contour positions beyond support unchanged; separate CGImage Vision point locations cross-check the CIImage production path.",
|
||||
"local_logs": [
|
||||
"build/analysis-jaw-short/core-tests.txt",
|
||||
"build/analysis-jaw-short/real-frames-asan.txt"
|
||||
],
|
||||
"app_built": false,
|
||||
"host_operated": false
|
||||
},
|
||||
"pre_fix_difference": {
|
||||
"source": "PGFaceProcessor.m; PGBeautyRenderer only does appearance",
|
||||
"jaw": "axis-aligned bbox jawBand, .20 multiplier, no radiusNeighbour or lip anchor",
|
||||
"short": "axis-aligned bbox lower field, .16 multiplier, no lower-contour polygon or nose axis",
|
||||
"bounds": "Difference in algorithm is established. It does not by itself prove why a specific installed screenshot looks like temple movement; coordinate/isolated-control localization must be checked in actual image output.",
|
||||
"processor_sha256": "fe4f2a6fc28799cc9b81832baaa2d6739320bde9def175ecc8a4494d004d6b28"
|
||||
},
|
||||
"air_excerpts": {
|
||||
"path": "docs/analysis/short-jaw-air.txt",
|
||||
"sha256": "f4ade84fdb2303515dab7b4d7eca8a9c2241039b9e6428cd51ceffc32b3f6b9b",
|
||||
"formatNote": "Trailing whitespace removed from numbered excerpt; original AIR source SHA remains unchanged."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
# 短脸与下颌骨局部变形
|
||||
|
||||
本次将短脸与下颌骨从包围框近似场替换为目标产物中恢复的76点局部场。几何实现在`PGFaceProcessor.m`;`PGBeautyRenderer.m`负责外观,不是这次位置问题的修改点。
|
||||
|
||||
证据是目标ARM64二进制的只读CPU汇编、常量字节与已有Apple metal-objdump AIR。未运行原版APP或shader,未操作Motion/FCP。CPU证据见`rea-jaw-short.json`,带原始行号的GPU IR见`short-jaw-air.txt`,推导及来源摘要见`short-jaw-report.json`。
|
||||
|
||||
## 确认的规则
|
||||
|
||||
两项共用像素尺寸增益:
|
||||
|
||||
`gain = clamp(280 / max(|(P75-P59)*imageSize|, 2*|(P67-(P74+P60)/2)*imageSize|, 1), 1, 2.25)`。
|
||||
|
||||
距离为UV时,点与采样坐标统一采用全图归一化、Y向下;像素增益另用实际渲染宽高计算。产品内部76点采用Y向上,因此在同一个kernel中统一转换,未改宿主图像方向。
|
||||
|
||||
下颌骨按顺序使用六组`(origin, radiusNeighbour, directionAnchor)`:
|
||||
|
||||
`(72,71,29), (62,63,29), (71,70,29), (63,64,29), (70,71,29), (64,63,29)`。
|
||||
|
||||
半径是前两点的相邻轮廓距离,方向是`P29-origin`。每步令`weight=clamp(1-|q-origin|/radius,0,1)`,再依次更新`q=clamp(q-direction*ratio*weight,0,1)`。`ratio=clamp(jaw,0,1)*0.05*gain`。不能把半径替换为到嘴锚点的距离,也不能将六步合并为在同一原始坐标上求和。
|
||||
|
||||
短脸使用`P62...P72`构成的11边闭合下半脸区域。内部权重为1;外部权重按到最近线段的UV距离线性下降,在距离达到`|P46-P48|`时为0。位移轴为`P46-P48`(公开Vision的鼻梁点区域),`ratio=clamp(short,-1,1)*0.25*gain`,采样更新`q=clamp(q-axis*ratio*weight,0,1)`。正值的可见运动沿鼻梁方向收短,负值延长。半径不超过`0.0001`时跳过;线段投影的平方长度门限为`0.000001`。
|
||||
|
||||
CPU字节证据ID:`ev_d1b8a49fc62e384710bac234967c3e8cbf7c5127081c9b2c6f6a0bbd80b80967`。`0x10005f9a8`的double值为`0.05`;`0x10005f9e0`为`0.25`,`0x10005f9d0`和`0x10005f9f0`分别是短脸下限`-1`与缩放后下限`-0.25`。汇编ID:`ev_8bc31392c07788d2482546121297c3f177d527282275ef21fd3e5edef9921544`。结构字段`xiaegu`偏移12、`duanlian`偏移24,结合CPU写入与AIR读取顺序确认映射。
|
||||
|
||||
## 产品改动及边界
|
||||
|
||||
移除旧`jaw*0.20*jawBand`和`-shortFace*0.16*lower`。保留瘦脸17步、下巴3步的点位、系数和数学,新的局部顺序为`jaw6 → chin3 → short polygon`,仍在同一个CIWarpKernel内更新采样坐标,对每张输入图像或蒙版,每张脸仅进行一次重采样。图像、磨皮/肤色/牙齿/眼睛四张蒙版调用同一变形路径。没有完整合法76点时,这四项恢复算法安全直通;旧记录、其他参数和离线归档继续可读。
|
||||
|
||||
原版完整顺序为`thin → narrow → V → jaw → cheek → chin → short → small-head → eyes`。产品其余五项仍采用原有近似body/眼睛场:body先于jaw,故组合全链不宣称原版等价。原版CPU另接受共同形变强度;本产品该乘数固定为1,未映射到任何用户参数。`PGAmount`/`s.amount`仅表示磨皮强度,不参与thin/chin/jaw/short比例;四项共用同一像素尺寸gain。用户分别调节各项脸型值,最终`PGMix`由宿主适配器独立混合总输出。没有新增共同形变强度控制,以保持磨皮、美白和脸型卡片各自的含义。这里没有证实或添加3D姿态识别、yaw衰减或五官恢复锁定。
|
||||
|
||||
旧算法与原版不同是确定事实;单凭静态代码,不能替某张已安装宿主画面的“太阳穴变化”断言唯一原因。下述位置验证针对真实PNG,宿主适配和原生交互由父任务验收。
|
||||
|
||||
## 验证
|
||||
|
||||
`scripts/test.sh`:Beauty49、Face116、Slim796,共961项通过。旧394项thin/chin测试保留;新增独立double oracle从上述AIR规则写成,比较实际GPU坐标场,最大UV误差`1.09828022e-05`。覆盖非零extent、横竖画幅、侧脸投影与roll、正负短脸、小脸增益上限、相邻轮廓半径反例、组合顺序、缺失/非法76点和四张蒙版一致性。
|
||||
|
||||
ASan专项以NASA公开肖像及本地用户正面/侧脸帧运行:886项通过,无ASan诊断。独立CGImage Vision请求对照了瞳孔、嘴唇、鼻梁和上下轮廓索引,防止与生产CIImage路径共用一次Y翻转假设。实际GPU采样场确认下半脸有响应,处于支持域外的瞳孔和上部轮廓位置不响应。
|
||||
|
||||
用户帧、完整坐标和测试构建只留在忽略的`build/`目录,不进入证据文件。上述不代表原版逐像素一致,也未替代新版本Motion/FCP原生验收;本次未构建APP、安装或提交。
|
||||
@@ -0,0 +1,25 @@
|
||||
# 自定义面板交互连接修复
|
||||
|
||||
1.1.0在FCP原生界面复现:卡片能显示,“更多设置”可展开,但全部参数控件禁用,底部显示“等待宿主连接”。这不是整张视图不接收事件。
|
||||
|
||||
控制器原先在startAction之前检查FxParameterRetrievalAPI_v6与FxParameterSettingAPI_v5,若宿主仅在action上下文中提供它们,就根本进不了action。1.1.1改为先取得FxCustomParameterActionAPI_v4,以真实PGInspectorView为sender开启操作,再获取读写接口;缺接口时正确结束操作,后续可重试。NSView sender也符合本机官方头文件的sender定义。
|
||||
|
||||
实际FCP正式安装版日志确认readBefore=0、writeBefore=0,进入action后readInAction=1、writeInAction=1,viewSender=1、onMain=1。界面控件随即启用,原生重置按钮能把肤色亮度清零;数值输入37并按Return后,滑块与数值显示同步为0.37/37%。因此原先门控条件在实际宿主中确实无法成立。
|
||||
|
||||
新增模拟宿主只有在action内才提供参数接口,并要求sender为NSView:修改前141项检查有5项失败,修改后141项全部通过;独立远端恢复的同一测试启用ASan通过。完整1046项回归通过。签名代码节点为53ce88dd15983e9397ab08f5ec9ad608fce5d0b5,正式中间版1.1.1(10)已安装并通过签名、公证和票据。后续用户报告短脸和下颌骨位置错误,继续在合并修复版完成两宿主最终验收,未给中间版建立最终发布标签。
|
||||
|
||||
## 原生验收的边界
|
||||
|
||||
Mouse/keyboard自动化尝试中有焦点落回时间线及参数在非本次操作时改变的情况,不能据此断言点击坐标一定错位。另一次FCP高CPU与AX超时,调用栈落在大量效果浏览器的辅助功能遍历;插件主线程大多空闲。收起效果浏览器后AX读取从约10秒降到约1秒。这不是已证明的插件死锁。原始截图、用户测试片段和全部窗口树保留本机build/native-111,不上传远程。
|
||||
|
||||
原版UI的只读对照显示isFlipped同为YES,原生滑块同样使用super mouseDown;root鼠标以locationInWindow经convertPoint:fromView:nil转换。名字含WidthFix的方法只处理容器高度不足,没有鼠标Y翻转或backing像素重映射。没有证据需要盲改本产品的isFlipped。1.1.2最终Motion/FCP数值、重置、保存重开及画面响应将另行记录。
|
||||
|
||||
## 数值失焦与拖动连续预览
|
||||
|
||||
原生build11验收发现数值Return后仍保留字段编辑器,失焦通知无条件再次提交旧文本,可能把宿主刚撤销或重置的状态写回。新增独立回归确认旧delegate在失焦时复活旧值;现追踪尚未提交的真实文本改动,Return只提交一次并释放AppKit字段编辑器,失焦仅提交新改动。离屏真实NSWindow/field editor用例已覆盖。
|
||||
|
||||
用户另报滑杆只在松手后更新:旧view层在mouseDown之前beginInteraction,整个NSSlider tracking loop结束才endInteraction,虽然continuous为YES,宿主可直到endAction才发布预览。现在gesture depth只阻止定时刷新覆盖拖动显示,每个有效slider action由controller单独开启/结束宿主上下文,仍先在真实NSView sender的action内取得API,完整自定义状态和隐藏镜像一起提交。没有新增手动分析或强制刷新按钮。
|
||||
|
||||
在仅于endAction发布预览的模拟宿主中,三个中间步进旧版3项失败,新版155项检查器ASan全部通过,含无变化去重、action配对、每步可读的完整渲染设置。宿主是否合并快速更新以及实际显示帧率由其调度和设备性能决定;不保证每一个鼠标事件均在屏幕独立显示一帧。新版原生复验另记。
|
||||
|
||||
远程编辑器补充:Motion build12中数值提交后的重置已经改变实际slider/输出,但仍保留字段编辑器并显示旧文本。最终改为对已提交编辑调用abortEditing,并仅在存在尚未提交的真实输入时阻止宿主刷新/保留文本;单纯残留焦点不再遮蔽撤销或重置。新增两个真实离屏编辑器用例确认无改动焦点可刷新、尚未提交输入仍受保护;检查器ASan现157项通过。
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"generator": "built-in image_gen",
|
||||
"type": "generated promotional artwork, not an actual effect-output comparison",
|
||||
"style": "宋式青绿古风;成年女性面部近景;金色瘦脸轮廓线与内收箭头",
|
||||
"thumbnail_text": [
|
||||
"大鱼人像美化",
|
||||
"自动瘦脸",
|
||||
"自动跟随",
|
||||
"自动美颜",
|
||||
"免费开源"
|
||||
],
|
||||
"app_card_text": [
|
||||
"大鱼人像美化",
|
||||
"免费开源",
|
||||
"自动注册 · 无需激活码",
|
||||
"启动 3 秒后自动退出",
|
||||
"Final Cut Pro · Motion",
|
||||
"dayu.media"
|
||||
],
|
||||
"files": [
|
||||
{
|
||||
"path": "Assets/Artwork/portrait-cover.png",
|
||||
"sha256": "a346f06e7793b4e5483ccd4b3b86f34c2036bbe6bbddb7ad9cc70c47e7d014ab"
|
||||
},
|
||||
{
|
||||
"path": "beatygril/beatygril/Wrapper Application/Resources/LauncherInfo.png",
|
||||
"sha256": "239552eeea738a32fafa70922d3cd1d4c115ff75d672574fa4c5acd941381fba"
|
||||
},
|
||||
{
|
||||
"path": "Templates/大鱼人像美化/large.png",
|
||||
"sha256": "a02ddb255b1ae255c749bb837a759e2dee1c3b8ffb9bdc1167957761bd5a1b97"
|
||||
},
|
||||
{
|
||||
"path": "Templates/大鱼人像美化/small.png",
|
||||
"sha256": "1552b838dd88f60dee0795e20aaca652f9c558ecefc93500e895fbff77c2a4d5"
|
||||
}
|
||||
],
|
||||
"thumbnail_lock": "uchg on both PNGs only; installer must restore flags",
|
||||
"template_sha256": "e9fa97dc14323d0daa7d3d61b06a78ba4000a70ab9f5e67ebffd750d49df1e15",
|
||||
"revision": "User feedback: reduce three feature lines by approximately one third and increase negative space."
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"mode": "built-in image_gen",
|
||||
"cover_prompt": "Use case: ads-marketing. Asset type: final 16:9 wide Chinese classical portrait beautification FxPlug product cover and very small Final Cut Pro effect thumbnail.\nEdit the supplied previous cover. Preserve its original jade-green, ivory and antique-gold Song-dynasty Chinese painting direction and the fictional adult Chinese hanfu woman identity, but redesign the composition for thumbnail legibility.\nPrimary request: CLOSE-UP FACE portrait dominates the RIGHT 52 percent; crop from the ornate updo to just below shoulders, face itself very large (about 65 percent of image height), almost frontal with slight elegant three-quarter angle, both cheeks and jaw clearly visible, natural skin texture and beautiful natural proportion. No fan, no hands, no full-body or waist framing. Gold/jade hairpin and small part of hanfu collar retain classical character. Background quiet dark jade and understated distant misty Chinese mountains; remove busy koi and flowers near lettering.\nDraw precise elegant luminous antique-gold contour curves along BOTH cheeks and jaw (following the skin outline, from below cheekbones toward chin) plus two short inward arrows on each side, unmistakably communicating automatic face slimming. Add a few restrained dotted jade landmark points and four fine rounded face-following corner marks. These overlays are design graphics only and must not distort the portrait. Avoid modern blue sci-fi HUD, grids covering eyes, and competitor likeness.\nLEFT 46 percent: extremely clear bold warm ivory Chinese display lettering, restrained gold border, generous spacing and high contrast. Text verbatim and only:\nTop compact brand line: \"大鱼人像美化\"\nThree equally prominent large feature lines stacked vertically:\n\"自动瘦脸\"\n\"自动跟随\"\n\"自动美颜\"\nSmall vermilion seal: \"免费开源\"\nSmall bottom line: \"Final Cut Pro · Motion\"\nNo tiny explanatory paragraphs and no extra slogans. Main three feature lines must be very large and remain distinguishable after downscaling to 192x108. Keep all key text and facial features inside 6 percent edge-safe margins. Refined classical poster with realistic close-up portrait, contemporary clear Chinese typography integrated into antique jade/gold aesthetics. Output a polished landscape 16:9 master image.\n",
|
||||
"final_refinement_prompt": "Edit this exact cover, changing ONLY the oversized typography layout. Preserve the classical jade-green/gold Chinese backdrop, the SAME adult hanfu woman's close-up face, all natural facial proportions, gold jaw-slimming contour arrows and tracking marks, 16:9 canvas, portrait placement. User says the feature text is too large.\nReduce the three feature lines \"自动瘦脸\", \"自动跟随\", \"自动美颜\" to approximately 62 percent of their current font height and width. Make the type refined, medium-weight warm ivory/gold rather than massive heavy gold brush blocks. Set them as three compact clean evenly spaced lines centered within the left column, with substantial calm dark-jade negative space above, below and around. Entire feature text group should occupy roughly 26 percent of total canvas width and 35 percent of canvas height, not the current 40 percent width and 57 percent height.\nKeep the product name \"大鱼人像美化\" at the top left but reduce it slightly (about 15 percent), preserving visual hierarchy: product name slightly larger than each feature line. Small red \"免费开源\" seal and small \"Final Cut Pro · Motion\" footer remain at bottom left. Remove the horizontal rules between the three feature lines; keep only subtle fine gold divider beneath product name and near footer. No new text. No giant lettering. Quiet elegant premium classical poster with FACE as the main visual focus. Exact required text retained: 大鱼人像美化 / 自动瘦脸 / 自动跟随 / 自动美颜 / 免费开源 / Final Cut Pro · Motion.\n"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
Beauty renderer: 49 checks, 0 failures (Metal).
|
||||
2026-10-21 22:48:21.085 face-tests[18460:5636636] FBBA: creating VNFaceBBoxAligner from VNFaceDetectorRevision2: VNFaceDetectorRevision2
|
||||
Face processing: 116 checks passed
|
||||
2026-10-21 22:48:24.576 slim-tests[18472:5636747] FBBA: creating VNFaceBBoxAligner from VNFaceDetectorRevision2: VNFaceDetectorRevision2
|
||||
PASS: 394 slim alignment checks; maximum UV oracle error 1.09828022e-05
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"status": "installed_user_qa_pending",
|
||||
"bundleIdentifier": "com.dayu.beatygril",
|
||||
"installationPath": "/Library/Plug-Ins/FxPlug/beatygril.app",
|
||||
"options": {
|
||||
"waitForHosts": true,
|
||||
"userTests": true
|
||||
},
|
||||
"nativeEffectQA": {
|
||||
"status": "pending_user",
|
||||
"performedByScript": false
|
||||
},
|
||||
"source": {
|
||||
"commit": "f79d24eb0185f31feaae58b83b632f92dcb3ca91",
|
||||
"trackedWorktreeChanges": []
|
||||
},
|
||||
"updatedAt": "2026-10-21T15:02:07.218005+00:00",
|
||||
"version": "1.1.0",
|
||||
"build": "9",
|
||||
"stagingSignature": "Executable=/private/tmp/paintfcp-build-20261021/Build/Products/Release/beatygril.app/Contents/MacOS/beatygril\nIdentifier=com.dayu.beatygril\nFormat=app bundle with Mach-O universal (x86_64 arm64)\nCodeDirectory v=20500 size=478 flags=0x10000(runtime) hashes=4+7 location=embedded\nHash type=sha256 size=32\nCandidateCDHash sha256=3611af053e7fa8c0969e2bb6f600b7e9775b1f00\nCandidateCDHashFull sha256=3611af053e7fa8c0969e2bb6f600b7e9775b1f000a0142f2f50b833a1fc2b494\nHash choices=sha256\nCMSDigest=3611af053e7fa8c0969e2bb6f600b7e9775b1f000a0142f2f50b833a1fc2b494\nCMSDigestType=2\nCDHash=3611af053e7fa8c0969e2bb6f600b7e9775b1f00\nSignature size=8968\nAuthority=Developer ID Application: zhong luo (B3W25FX5H5)\nAuthority=Developer ID Certification Authority\nAuthority=Apple Root CA\nTimestamp=Oct 21, 2026 at 22:53:52\nInfo.plist entries=22\nTeamIdentifier=B3W25FX5H5\nRuntime Version=27.0.0\nSealed Resources version=2 rules=13 files=4\nInternal requirements count=1 size=180\nTotal signatures=1\nChosen signature=1\n",
|
||||
"notarization": {
|
||||
"id": "ce26be63-9dcd-436a-bcb5-bd294fcc92a9",
|
||||
"status": "Accepted",
|
||||
"message": "Processing complete"
|
||||
},
|
||||
"signingVerified": true,
|
||||
"stapleValidated": true,
|
||||
"stagingPluginUnregistered": true,
|
||||
"runningHosts": [],
|
||||
"installedSignature": "Executable=/Library/Plug-Ins/FxPlug/beatygril.app/Contents/MacOS/beatygril\nIdentifier=com.dayu.beatygril\nFormat=app bundle with Mach-O universal (x86_64 arm64)\nCodeDirectory v=20500 size=478 flags=0x10000(runtime) hashes=4+7 location=embedded\nHash type=sha256 size=32\nCandidateCDHash sha256=3611af053e7fa8c0969e2bb6f600b7e9775b1f00\nCandidateCDHashFull sha256=3611af053e7fa8c0969e2bb6f600b7e9775b1f000a0142f2f50b833a1fc2b494\nHash choices=sha256\nCMSDigest=3611af053e7fa8c0969e2bb6f600b7e9775b1f000a0142f2f50b833a1fc2b494\nCMSDigestType=2\nCDHash=3611af053e7fa8c0969e2bb6f600b7e9775b1f00\nSignature size=8968\nAuthority=Developer ID Application: zhong luo (B3W25FX5H5)\nAuthority=Developer ID Certification Authority\nAuthority=Apple Root CA\nTimestamp=Oct 21, 2026 at 22:53:52\nNotarization Ticket=stapled\nInfo.plist entries=22\nTeamIdentifier=B3W25FX5H5\nRuntime Version=27.0.0\nSealed Resources version=2 rules=13 files=4\nInternal requirements count=1 size=180\nTotal signatures=1\nChosen signature=1\n",
|
||||
"installedSignatureVerified": true,
|
||||
"installedStapleValidated": true,
|
||||
"gatekeeperAccepted": true,
|
||||
"pluginDiscovery": {
|
||||
"bundleIdentifier": "com.dayu.beatygril.fxplug",
|
||||
"formalPath": "/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit",
|
||||
"output": " com.dayu.beatygril.fxplug(1.1)\tDA452CD1-7B7F-4F15-9C52-09270028E946\t2026-10-21 15:02:07 +0000\t/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit\n (1 plug-in)\n"
|
||||
},
|
||||
"removedGeneratedPaths": [
|
||||
"/Users/dayu/Documents/PaintFCP/build/delivery/previous-beatygril.app",
|
||||
"/tmp/paintfcp-build-20261021",
|
||||
"/Users/dayu/Documents/PaintFCP/build/delivery/notarization.zip"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
2026-10-21 22:52:28.194 host-tests[18824:5644007] FBBA: creating VNFaceBBoxAligner from VNFaceDetectorRevision2: VNFaceDetectorRevision2
|
||||
Default IOSurface maximum byte difference: 0
|
||||
Alternate destination tag maximum byte difference: 0
|
||||
Anatomy default 0/0 max=0 mean=0.0000000 eye=0/0 chin=0 inverted=0
|
||||
Anatomy default 0/1 max=0 mean=0.0000000 eye=0/0 chin=0 inverted=0
|
||||
Anatomy default 1/0 max=0 mean=0.0000000 eye=0/0 chin=0 inverted=0
|
||||
Anatomy default 1/1 max=0 mean=0.0000000 eye=0/0 chin=0 inverted=0
|
||||
Anatomy big-eye 0/0 max=2 mean=0.0008917 eye=33510/33880 chin=0 inverted=0
|
||||
Anatomy big-eye 0/1 max=2 mean=0.0008917 eye=33510/33880 chin=0 inverted=0
|
||||
Anatomy big-eye 1/0 max=2 mean=0.0008917 eye=33510/33880 chin=0 inverted=0
|
||||
Anatomy big-eye 1/1 max=2 mean=0.0008917 eye=33510/33880 chin=0 inverted=0
|
||||
Anatomy chin-positive 0/0 max=1 mean=0.0001459 eye=0/0 chin=116812 inverted=0
|
||||
Anatomy chin-positive 0/1 max=1 mean=0.0001459 eye=0/0 chin=116812 inverted=0
|
||||
Anatomy chin-positive 1/0 max=1 mean=0.0001459 eye=0/0 chin=116812 inverted=0
|
||||
Anatomy chin-positive 1/1 max=1 mean=0.0001459 eye=0/0 chin=116812 inverted=0
|
||||
Anatomy chin-negative 0/0 max=1 mean=0.0000849 eye=0/0 chin=60426 inverted=0
|
||||
Anatomy chin-negative 0/1 max=1 mean=0.0000849 eye=0/0 chin=60426 inverted=0
|
||||
Anatomy chin-negative 1/0 max=1 mean=0.0000849 eye=0/0 chin=60426 inverted=0
|
||||
Anatomy chin-negative 1/1 max=1 mean=0.0000849 eye=0/0 chin=60426 inverted=0
|
||||
Legacy fallback upright comparison: max=2 mean=0.0010357
|
||||
Host adapter: 248 checks, 0 failures
|
||||
二进制文件未显示。
|
之后 宽度: | 高度: | 大小: 482 KiB |
@@ -0,0 +1 @@
|
||||
Inspector: 134 checks, 0 failures
|
||||
@@ -0,0 +1,2 @@
|
||||
com.dayu.beatygril.fxplug(1.1) DA452CD1-7B7F-4F15-9C52-09270028E946 2026-10-21 15:02:07 +0000 /Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit
|
||||
(1 plug-in)
|
||||
@@ -0,0 +1,24 @@
|
||||
Executable=/Library/Plug-Ins/FxPlug/beatygril.app/Contents/MacOS/beatygril
|
||||
Identifier=com.dayu.beatygril
|
||||
Format=app bundle with Mach-O universal (x86_64 arm64)
|
||||
CodeDirectory v=20500 size=478 flags=0x10000(runtime) hashes=4+7 location=embedded
|
||||
Hash type=sha256 size=32
|
||||
CandidateCDHash sha256=3611af053e7fa8c0969e2bb6f600b7e9775b1f00
|
||||
CandidateCDHashFull sha256=3611af053e7fa8c0969e2bb6f600b7e9775b1f000a0142f2f50b833a1fc2b494
|
||||
Hash choices=sha256
|
||||
CMSDigest=3611af053e7fa8c0969e2bb6f600b7e9775b1f000a0142f2f50b833a1fc2b494
|
||||
CMSDigestType=2
|
||||
CDHash=3611af053e7fa8c0969e2bb6f600b7e9775b1f00
|
||||
Signature size=8968
|
||||
Authority=Developer ID Application: zhong luo (B3W25FX5H5)
|
||||
Authority=Developer ID Certification Authority
|
||||
Authority=Apple Root CA
|
||||
Timestamp=Oct 21, 2026 at 22:53:52
|
||||
Notarization Ticket=stapled
|
||||
Info.plist entries=22
|
||||
TeamIdentifier=B3W25FX5H5
|
||||
Runtime Version=27.0.0
|
||||
Sealed Resources version=2 rules=13 files=4
|
||||
Internal requirements count=1 size=180
|
||||
Total signatures=1
|
||||
Chosen signature=1
|
||||
@@ -0,0 +1,91 @@
|
||||
{
|
||||
"version": "1.1.0",
|
||||
"build": "9",
|
||||
"date": "2026-10-21T15:03:33.815750+00:00",
|
||||
"sourceCommit": "f79d24eb0185f31feaae58b83b632f92dcb3ca91",
|
||||
"uiOperations": false,
|
||||
"hostTerminationPerformed": false,
|
||||
"nativeVisualQA": "pending user",
|
||||
"installationPath": "/Library/Plug-Ins/FxPlug/beatygril.app",
|
||||
"codesign": {
|
||||
"exitCode": 0,
|
||||
"stdout": "",
|
||||
"stderr": ""
|
||||
},
|
||||
"staple": {
|
||||
"exitCode": 0,
|
||||
"stdout": "Processing: /Library/Plug-Ins/FxPlug/beatygril.app\nThe validate action worked!",
|
||||
"stderr": ""
|
||||
},
|
||||
"gatekeeper": {
|
||||
"exitCode": 0,
|
||||
"stdout": "",
|
||||
"stderr": "/Library/Plug-Ins/FxPlug/beatygril.app: accepted\nsource=Notarized Developer ID"
|
||||
},
|
||||
"identity": [
|
||||
{
|
||||
"path": "/Library/Plug-Ins/FxPlug/beatygril.app",
|
||||
"bundleIdentifier": "com.dayu.beatygril",
|
||||
"version": "1.1.0",
|
||||
"build": "9",
|
||||
"archs": "x86_64 arm64",
|
||||
"signature": "Executable=/Library/Plug-Ins/FxPlug/beatygril.app/Contents/MacOS/beatygril\nIdentifier=com.dayu.beatygril\nFormat=app bundle with Mach-O universal (x86_64 arm64)\nCodeDirectory v=20500 size=478 flags=0x10000(runtime) hashes=4+7 location=embedded\nHash type=sha256 size=32\nCandidateCDHash sha256=3611af053e7fa8c0969e2bb6f600b7e9775b1f00\nCandidateCDHashFull sha256=3611af053e7fa8c0969e2bb6f600b7e9775b1f000a0142f2f50b833a1fc2b494\nHash choices=sha256\nCMSDigest=3611af053e7fa8c0969e2bb6f600b7e9775b1f000a0142f2f50b833a1fc2b494\nCMSDigestType=2\nCDHash=3611af053e7fa8c0969e2bb6f600b7e9775b1f00\nSignature size=8968\nAuthority=Developer ID Application: zhong luo (B3W25FX5H5)\nAuthority=Developer ID Certification Authority\nAuthority=Apple Root CA\nTimestamp=Oct 21, 2026 at 22:53:52\nNotarization Ticket=stapled\nInfo.plist entries=22\nTeamIdentifier=B3W25FX5H5\nRuntime Version=27.0.0\nSealed Resources version=2 rules=13 files=4\nInternal requirements count=1 size=180\nTotal signatures=1\nChosen signature=1"
|
||||
},
|
||||
{
|
||||
"path": "/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit",
|
||||
"bundleIdentifier": "com.dayu.beatygril.fxplug",
|
||||
"version": "1.1.0",
|
||||
"build": "9",
|
||||
"archs": "x86_64 arm64",
|
||||
"signature": "Executable=/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit/Contents/MacOS/beatygril XPC Service\nIdentifier=com.dayu.beatygril.fxplug\nFormat=bundle with Mach-O universal (x86_64 arm64)\nCodeDirectory v=20500 size=837 flags=0x10000(runtime) hashes=19+3 location=embedded\nHash type=sha256 size=32\nCandidateCDHash sha256=c9b6ba40f307b1e1134890cae999a46b7c51750b\nCandidateCDHashFull sha256=c9b6ba40f307b1e1134890cae999a46b7c51750b599514a0e5858a1fc9618926\nHash choices=sha256\nCMSDigest=c9b6ba40f307b1e1134890cae999a46b7c51750b599514a0e5858a1fc9618926\nCMSDigestType=2\nCDHash=c9b6ba40f307b1e1134890cae999a46b7c51750b\nSignature size=8969\nAuthority=Developer ID Application: zhong luo (B3W25FX5H5)\nAuthority=Developer ID Certification Authority\nAuthority=Apple Root CA\nTimestamp=Oct 21, 2026 at 22:53:51\nInfo.plist entries=26\nTeamIdentifier=B3W25FX5H5\nRuntime Version=27.0.0\nSealed Resources version=2 rules=13 files=4\nInternal requirements count=1 size=188\nTotal signatures=1\nChosen signature=1"
|
||||
}
|
||||
],
|
||||
"template": {
|
||||
"installedWithHostsClosed": true,
|
||||
"files": [
|
||||
{
|
||||
"path": "/Users/dayu/Movies/Motion Templates.localized/Effects.localized/大鱼/大鱼人像美化/大鱼人像美化.moef",
|
||||
"bytes": 23073,
|
||||
"sha256": "e1c0cff1dd3541ed97cab2cb39a76ad4f28dc659b3be7991cc7ac92dd8ea090b"
|
||||
},
|
||||
{
|
||||
"path": "/Users/dayu/Movies/Motion Templates.localized/Effects.localized/大鱼/大鱼人像美化/Drop Zone.tiff",
|
||||
"bytes": 420128,
|
||||
"sha256": "a7f8e3a2a431dbb3ce9e777040bb55e701cfe9ab9632e8cbb10359732bc8692e"
|
||||
}
|
||||
],
|
||||
"publicationCount": 3,
|
||||
"manualAnalysisPublished": false
|
||||
},
|
||||
"discovery": "com.dayu.beatygril.fxplug(1.1)\tDA452CD1-7B7F-4F15-9C52-09270028E946\t2026-10-21 15:02:07 +0000\t/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit\n (1 plug-in)",
|
||||
"duplicateInventory": {
|
||||
"scopes": [
|
||||
"/Users/dayu/Documents/PaintFCP",
|
||||
"/Users/dayu/.codex/worktrees",
|
||||
"/Users/dayu/Library/Developer/Xcode/DerivedData",
|
||||
"/Users/dayu/Desktop",
|
||||
"/Users/dayu/Downloads",
|
||||
"/Applications",
|
||||
"/Library/Plug-Ins/FxPlug",
|
||||
"/Users/dayu/Library/Plug-Ins/FxPlug",
|
||||
"/private/tmp/paintfcp-chin-smoke.rfco16hq",
|
||||
"/private/tmp/paintfcp-format-probe.PsxvYQ",
|
||||
"/private/tmp/paintfcp-live-review.ItH4EG",
|
||||
"/private/tmp/paintfcp-rea.b3j9Te",
|
||||
"/private/tmp/paintfcp-slim-field.6fDdJj",
|
||||
"/private/tmp/paintfcp-slim-smoke.3ncdDe",
|
||||
"/private/tmp/paintfcp-y-review.KpPi8T"
|
||||
],
|
||||
"matchingOuterApps": [
|
||||
"/Library/Plug-Ins/FxPlug/beatygril.app"
|
||||
],
|
||||
"errors": [],
|
||||
"scopeNote": "Bounded scan of project, worktrees, known build locations, Applications and FxPlug directories; not all mounted volumes."
|
||||
},
|
||||
"generatedCopyCleanup": {
|
||||
"/Users/dayu/Documents/PaintFCP/build/delivery/previous-beatygril.app": true,
|
||||
"/tmp/paintfcp-build-20261021": true,
|
||||
"/Users/dayu/Documents/PaintFCP/build/delivery/notarization.zip": true
|
||||
},
|
||||
"status": "installed, formal identity/ticket/discovery/template verified; user native QA pending"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"id":"ce26be63-9dcd-436a-bcb5-bd294fcc92a9","status":"Accepted","message":"Processing complete"}
|
||||
@@ -0,0 +1,21 @@
|
||||
2026-10-21 22:48:19.739 osc-tests[18452:5636580] PGFaceOSC point-write desired=(0.25,0.75) setter=(0.25,0.75) readback=(0.25,0.75) mode=normalized verified=1
|
||||
2026-10-21 22:48:19.745 osc-tests[18452:5636580] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(80,180) surface=320x240 origin=0
|
||||
2026-10-21 22:48:20.198 osc-tests[18452:5636580] PGFaceOSC point-write desired=(0.8,0.3) setter=(1152,-432) readback=(0.8,0.3) mode=centered-pixels verified=1
|
||||
2026-10-21 22:48:20.198 osc-tests[18452:5636580] PGFaceOSC coordinates selection=(0.7,0.4) objectBounds=(-1920,-1080,3840,2160) canvas=(152,78) surface=320x240 origin=0
|
||||
2026-10-21 22:48:20.246 osc-tests[18452:5636580] PGFaceOSC point-write desired=(0.8,0.3) setter=(0.8,0.3) readback=(0.12345,0.87654) mode=normalized verified=0
|
||||
2026-10-21 22:48:20.247 osc-tests[18452:5636580] PGFaceOSC point-write desired=(0.75,0.25) setter=(0.75,0.25) readback=(0.75,0.25) mode=normalized verified=1
|
||||
2026-10-21 22:48:20.247 osc-tests[18452:5636580] PGFaceOSC coordinates selection=(0.75,0.25) objectBounds=(-100,-50,200,100) canvas=(160,60) surface=320x240 origin=0
|
||||
2026-10-21 22:48:20.298 osc-tests[18452:5636580] PGFaceOSC point-write desired=(0.625,0.375) setter=(0.625,0.375) readback=(0.625,0.375) mode=normalized verified=1
|
||||
2026-10-21 22:48:20.298 osc-tests[18452:5636580] PGFaceOSC coordinates selection=(0.625,0.375) objectBounds=(-100,-50,200,100) canvas=(240,60) surface=320x240 origin=0
|
||||
2026-10-21 22:48:20.348 osc-tests[18452:5636580] PGFaceOSC point-write desired=(0.25,0.75) setter=(0.25,0.75) readback=(0.25,0.75) mode=normalized verified=1
|
||||
2026-10-21 22:48:20.349 osc-tests[18452:5636580] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(144,180) surface=320x240 origin=0
|
||||
2026-10-21 22:48:20.398 osc-tests[18452:5636580] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
|
||||
2026-10-21 22:48:20.398 osc-tests[18452:5636580] PGFaceOSC coordinates selection=(0,1) objectBounds=(-100,-50,200,100) canvas=(100,140) surface=320x240 origin=0
|
||||
2026-10-21 22:48:20.447 osc-tests[18452:5636580] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(nan,nan) surface=320x240 origin=0
|
||||
2026-10-21 22:48:20.500 osc-tests[18452:5636580] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
|
||||
2026-10-21 22:48:20.500 osc-tests[18452:5636580] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(inf,nan) surface=320x240 origin=0
|
||||
2026-10-21 22:48:20.546 osc-tests[18452:5636580] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
|
||||
2026-10-21 22:48:20.547 osc-tests[18452:5636580] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(80,nan) surface=320x240 origin=0
|
||||
2026-10-21 22:48:20.608 osc-tests[18452:5636580] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
|
||||
2026-10-21 22:48:20.608 osc-tests[18452:5636580] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
|
||||
OSC adapter: 98 checks, 0 failures
|
||||
@@ -0,0 +1 @@
|
||||
Inspector: 134 checks, 0 failures
|
||||
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"status": "structure_verified_native_loading_pending",
|
||||
"templateSHA256": "e1c0cff1dd3541ed97cab2cb39a76ad4f28dc659b3be7991cc7ac92dd8ea090b",
|
||||
"pluginVersion": "1.1.0",
|
||||
"publications": [
|
||||
{
|
||||
"object": "510000011",
|
||||
"channel": "./2/100",
|
||||
"name": "FCP 版本作者→"
|
||||
},
|
||||
{
|
||||
"object": "510000012",
|
||||
"channel": "./2/100",
|
||||
"name": "更多模板浏览器访问→"
|
||||
},
|
||||
{
|
||||
"object": "510000005",
|
||||
"channel": "./210",
|
||||
"name": ""
|
||||
}
|
||||
],
|
||||
"parameterPathsResolved": 50,
|
||||
"sourceMediaClosed": true,
|
||||
"mediaSHA256": "a7f8e3a2a431dbb3ce9e777040bb55e701cfe9ab9632e8cbb10359732bc8692e",
|
||||
"customState": {
|
||||
"class": "PGInspectorState",
|
||||
"schema": 1,
|
||||
"hasEdits": false,
|
||||
"values": {
|
||||
"41": 0.0,
|
||||
"30": 1.0,
|
||||
"71": 1.0,
|
||||
"27": 0.0,
|
||||
"5": 1.0,
|
||||
"35": 0.0,
|
||||
"24": 0.0,
|
||||
"2": 0.0,
|
||||
"32": 0.0,
|
||||
"40": 1.7999999523162842,
|
||||
"70": 1.0,
|
||||
"18": 0.0,
|
||||
"29": 0.0,
|
||||
"26": 0.0,
|
||||
"15": 0.0,
|
||||
"4": 0.0,
|
||||
"23": 0.0,
|
||||
"12": 0.0,
|
||||
"42": 0.0,
|
||||
"72": 1.0,
|
||||
"31": 0.0,
|
||||
"1": 1.0,
|
||||
"39": 0.0,
|
||||
"28": 0.0,
|
||||
"17": 0.0,
|
||||
"25": 0.0,
|
||||
"14": 0.0,
|
||||
"3": 8.0,
|
||||
"33": 0.0,
|
||||
"22": 0.0
|
||||
},
|
||||
"disabled": [],
|
||||
"classes": [
|
||||
"NSDictionary",
|
||||
"NSSet",
|
||||
"PGInspectorState"
|
||||
],
|
||||
"innerBytes": 935,
|
||||
"innerSHA256": "257540ac6079756a76f3d31dff1b9bca2b33393962057927791316965495ab60",
|
||||
"outerBytes": 1127,
|
||||
"outerSHA256": "95d5f1e2c35c40e9f1234b1480894bcc0ee68c0822c9bd3f7c04338062e6a26b",
|
||||
"encodedCharacters": 1516,
|
||||
"exactTextRoundtrip": true
|
||||
},
|
||||
"nativeLoading": "pending user; no host executed"
|
||||
}
|
||||
文件差异过大,未显示。
加载差异
@@ -0,0 +1,11 @@
|
||||
Target: f79d24eb0185f31feaae58b83b632f92dcb3ca91
|
||||
Remote: https://dayu.media/git/dayu/paintfcp-fxplug.git
|
||||
Passed: True
|
||||
Tracked files: 111
|
||||
All tracked bytes/blob IDs/modes: True
|
||||
Git fsck --strict --full: 0
|
||||
LFS/submodules: [] / []
|
||||
Inspector tests: {'checks': 134, 'failures': 0, 'ASan': True, 'exitCode': 0, 'log': 'inspector-tests-asan.txt'}
|
||||
Template audit: structure_verified_native_loading_pending
|
||||
Core 559 tests not repeated; no APP build/host/UI/sign/install.
|
||||
Temporary clone removed: True
|
||||
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"status": "structure_verified_native_loading_pending",
|
||||
"templateSHA256": "e1c0cff1dd3541ed97cab2cb39a76ad4f28dc659b3be7991cc7ac92dd8ea090b",
|
||||
"pluginVersion": "1.1.0",
|
||||
"publications": [
|
||||
{
|
||||
"object": "510000011",
|
||||
"channel": "./2/100",
|
||||
"name": "FCP 版本作者→"
|
||||
},
|
||||
{
|
||||
"object": "510000012",
|
||||
"channel": "./2/100",
|
||||
"name": "更多模板浏览器访问→"
|
||||
},
|
||||
{
|
||||
"object": "510000005",
|
||||
"channel": "./210",
|
||||
"name": ""
|
||||
}
|
||||
],
|
||||
"parameterPathsResolved": 50,
|
||||
"sourceMediaClosed": true,
|
||||
"mediaSHA256": "a7f8e3a2a431dbb3ce9e777040bb55e701cfe9ab9632e8cbb10359732bc8692e",
|
||||
"customState": {
|
||||
"class": "PGInspectorState",
|
||||
"schema": 1,
|
||||
"hasEdits": false,
|
||||
"values": {
|
||||
"41": 0.0,
|
||||
"30": 1.0,
|
||||
"71": 1.0,
|
||||
"27": 0.0,
|
||||
"5": 1.0,
|
||||
"35": 0.0,
|
||||
"24": 0.0,
|
||||
"2": 0.0,
|
||||
"32": 0.0,
|
||||
"40": 1.7999999523162842,
|
||||
"70": 1.0,
|
||||
"18": 0.0,
|
||||
"29": 0.0,
|
||||
"26": 0.0,
|
||||
"15": 0.0,
|
||||
"4": 0.0,
|
||||
"23": 0.0,
|
||||
"12": 0.0,
|
||||
"42": 0.0,
|
||||
"72": 1.0,
|
||||
"31": 0.0,
|
||||
"1": 1.0,
|
||||
"39": 0.0,
|
||||
"28": 0.0,
|
||||
"17": 0.0,
|
||||
"25": 0.0,
|
||||
"14": 0.0,
|
||||
"3": 8.0,
|
||||
"33": 0.0,
|
||||
"22": 0.0
|
||||
},
|
||||
"disabled": [],
|
||||
"classes": [
|
||||
"NSDictionary",
|
||||
"NSSet",
|
||||
"PGInspectorState"
|
||||
],
|
||||
"innerBytes": 935,
|
||||
"innerSHA256": "257540ac6079756a76f3d31dff1b9bca2b33393962057927791316965495ab60",
|
||||
"outerBytes": 1127,
|
||||
"outerSHA256": "95d5f1e2c35c40e9f1234b1480894bcc0ee68c0822c9bd3f7c04338062e6a26b",
|
||||
"encodedCharacters": 1516,
|
||||
"exactTextRoundtrip": true
|
||||
},
|
||||
"nativeLoading": "pending user; no host executed"
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"scope": "offscreen AppKit inspector, real production controller and mocked host callbacks; no Motion/FCP UI",
|
||||
"fullChecks": 1039,
|
||||
"counts": {
|
||||
"beauty": 49,
|
||||
"face": 116,
|
||||
"thinChin": 394,
|
||||
"hostAdapterASan": 248,
|
||||
"oscASan": 98,
|
||||
"inspectorASan": 134
|
||||
},
|
||||
"nativeVisualQA": "pending user",
|
||||
"manualAnalysisUI": "removed; legacy IDs hidden and buttons disabled",
|
||||
"renderMode": "realtime automatic follow; stored manual geometry never used",
|
||||
"templatePublishedEntries": 3,
|
||||
"offscreenWidths": [
|
||||
280,
|
||||
320,
|
||||
380,
|
||||
520
|
||||
],
|
||||
"independentViewChecks": 27,
|
||||
"fixedRegressions": [
|
||||
"shape group bypass clears face-box overlay",
|
||||
"disabled legacy detail effects stay disabled on first edit",
|
||||
"legacy global smoothing remains enabled when its old local group was off",
|
||||
"new gesture reads current host after immediate undo",
|
||||
"pending synchronization is bound to CMTime",
|
||||
"same-time host change refreshes without waiting",
|
||||
"explicit numeric input is never skipped due to stale UI cache"
|
||||
],
|
||||
"archives": "Own PGInspectorState secure payload; Foundation outer and inner decode and Motion alphabet roundtrip tested"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
Beauty renderer: 49 checks, 0 failures (Metal).
|
||||
2026-10-21 23:20:16.950 face-tests[22007:5696503] FBBA: creating VNFaceBBoxAligner from VNFaceDetectorRevision2: VNFaceDetectorRevision2
|
||||
Face processing: 116 checks passed
|
||||
2026-10-21 23:20:21.817 slim-tests[22045:5696731] FBBA: creating VNFaceBBoxAligner from VNFaceDetectorRevision2: VNFaceDetectorRevision2
|
||||
PASS: 394 slim alignment checks; maximum UV oracle error 1.09828022e-05
|
||||
@@ -0,0 +1,21 @@
|
||||
2026-10-21 23:20:18.015 host-tests[22024:5696629] FBBA: creating VNFaceBBoxAligner from VNFaceDetectorRevision2: VNFaceDetectorRevision2
|
||||
Default IOSurface maximum byte difference: 0
|
||||
Alternate destination tag maximum byte difference: 0
|
||||
Anatomy default 0/0 max=0 mean=0.0000000 eye=0/0 chin=0 inverted=0
|
||||
Anatomy default 0/1 max=0 mean=0.0000000 eye=0/0 chin=0 inverted=0
|
||||
Anatomy default 1/0 max=0 mean=0.0000000 eye=0/0 chin=0 inverted=0
|
||||
Anatomy default 1/1 max=0 mean=0.0000000 eye=0/0 chin=0 inverted=0
|
||||
Anatomy big-eye 0/0 max=2 mean=0.0008917 eye=33510/33880 chin=0 inverted=0
|
||||
Anatomy big-eye 0/1 max=2 mean=0.0008917 eye=33510/33880 chin=0 inverted=0
|
||||
Anatomy big-eye 1/0 max=2 mean=0.0008917 eye=33510/33880 chin=0 inverted=0
|
||||
Anatomy big-eye 1/1 max=2 mean=0.0008917 eye=33510/33880 chin=0 inverted=0
|
||||
Anatomy chin-positive 0/0 max=1 mean=0.0001459 eye=0/0 chin=116812 inverted=0
|
||||
Anatomy chin-positive 0/1 max=1 mean=0.0001459 eye=0/0 chin=116812 inverted=0
|
||||
Anatomy chin-positive 1/0 max=1 mean=0.0001459 eye=0/0 chin=116812 inverted=0
|
||||
Anatomy chin-positive 1/1 max=1 mean=0.0001459 eye=0/0 chin=116812 inverted=0
|
||||
Anatomy chin-negative 0/0 max=1 mean=0.0000849 eye=0/0 chin=60426 inverted=0
|
||||
Anatomy chin-negative 0/1 max=1 mean=0.0000849 eye=0/0 chin=60426 inverted=0
|
||||
Anatomy chin-negative 1/0 max=1 mean=0.0000849 eye=0/0 chin=60426 inverted=0
|
||||
Anatomy chin-negative 1/1 max=1 mean=0.0000849 eye=0/0 chin=60426 inverted=0
|
||||
Legacy fallback upright comparison: max=2 mean=0.0010357
|
||||
Host adapter: 248 checks, 0 failures
|
||||
@@ -0,0 +1,6 @@
|
||||
FAIL: an action-scoped host connects and reads the actual template values
|
||||
FAIL: custom parameter actions identify their actual inspector NSView
|
||||
FAIL: action-scoped parameter APIs do not leave the whole native panel disabled
|
||||
FAIL: a connected native control writes through the action-scoped setting API
|
||||
FAIL: a later host connection restores controls after a transient API failure
|
||||
Inspector: 141 checks, 5 failures
|
||||
@@ -0,0 +1 @@
|
||||
Inspector: 141 checks, 0 failures
|
||||
@@ -0,0 +1 @@
|
||||
Inspector: 141 checks, 0 failures
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"status": "failed",
|
||||
"bundleIdentifier": "com.dayu.beatygril",
|
||||
"installationPath": "/Library/Plug-Ins/FxPlug/beatygril.app",
|
||||
"options": {
|
||||
"waitForHosts": true,
|
||||
"userTests": false
|
||||
},
|
||||
"nativeEffectQA": {
|
||||
"status": "pending_operator",
|
||||
"performedByScript": false
|
||||
},
|
||||
"source": {
|
||||
"commit": "53ce88dd15983e9397ab08f5ec9ad608fce5d0b5",
|
||||
"trackedWorktreeChanges": []
|
||||
},
|
||||
"updatedAt": "2026-10-21T15:40:33.591925+00:00",
|
||||
"version": "1.1.1",
|
||||
"build": "10",
|
||||
"stagingSignature": "Executable=/private/tmp/paintfcp-build-20261021/Build/Products/Release/beatygril.app/Contents/MacOS/beatygril\nIdentifier=com.dayu.beatygril\nFormat=app bundle with Mach-O universal (x86_64 arm64)\nCodeDirectory v=20500 size=478 flags=0x10000(runtime) hashes=4+7 location=embedded\nHash type=sha256 size=32\nCandidateCDHash sha256=3b65dadb35fe03201c99ce39fb1a41bb3ed6b00a\nCandidateCDHashFull sha256=3b65dadb35fe03201c99ce39fb1a41bb3ed6b00a3b65e0bd8d653434f77cad3a\nHash choices=sha256\nCMSDigest=3b65dadb35fe03201c99ce39fb1a41bb3ed6b00a3b65e0bd8d653434f77cad3a\nCMSDigestType=2\nCDHash=3b65dadb35fe03201c99ce39fb1a41bb3ed6b00a\nSignature size=8968\nAuthority=Developer ID Application: zhong luo (B3W25FX5H5)\nAuthority=Developer ID Certification Authority\nAuthority=Apple Root CA\nTimestamp=Oct 21, 2026 at 23:22:09\nInfo.plist entries=22\nTeamIdentifier=B3W25FX5H5\nRuntime Version=27.0.0\nSealed Resources version=2 rules=13 files=4\nInternal requirements count=1 size=180\nTotal signatures=1\nChosen signature=1\n",
|
||||
"notarization": {
|
||||
"message": "Processing complete",
|
||||
"status": "Accepted",
|
||||
"id": "a6218c05-b08f-417f-9617-c8e39b458a28"
|
||||
},
|
||||
"signingVerified": true,
|
||||
"stapleValidated": true,
|
||||
"stagingPluginUnregistered": true,
|
||||
"runningHosts": [],
|
||||
"installedSignature": "Executable=/Library/Plug-Ins/FxPlug/beatygril.app/Contents/MacOS/beatygril\nIdentifier=com.dayu.beatygril\nFormat=app bundle with Mach-O universal (x86_64 arm64)\nCodeDirectory v=20500 size=478 flags=0x10000(runtime) hashes=4+7 location=embedded\nHash type=sha256 size=32\nCandidateCDHash sha256=3b65dadb35fe03201c99ce39fb1a41bb3ed6b00a\nCandidateCDHashFull sha256=3b65dadb35fe03201c99ce39fb1a41bb3ed6b00a3b65e0bd8d653434f77cad3a\nHash choices=sha256\nCMSDigest=3b65dadb35fe03201c99ce39fb1a41bb3ed6b00a3b65e0bd8d653434f77cad3a\nCMSDigestType=2\nCDHash=3b65dadb35fe03201c99ce39fb1a41bb3ed6b00a\nSignature size=8968\nAuthority=Developer ID Application: zhong luo (B3W25FX5H5)\nAuthority=Developer ID Certification Authority\nAuthority=Apple Root CA\nTimestamp=Oct 21, 2026 at 23:22:09\nNotarization Ticket=stapled\nInfo.plist entries=22\nTeamIdentifier=B3W25FX5H5\nRuntime Version=27.0.0\nSealed Resources version=2 rules=13 files=4\nInternal requirements count=1 size=180\nTotal signatures=1\nChosen signature=1\n",
|
||||
"installedSignatureVerified": true,
|
||||
"installedStapleValidated": true,
|
||||
"gatekeeperAccepted": true,
|
||||
"pluginDiscovery": {
|
||||
"bundleIdentifier": "com.dayu.beatygril.fxplug",
|
||||
"formalPath": "/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit",
|
||||
"output": " com.dayu.beatygril.fxplug(1.1)\tDDFB35E6-8D3D-4436-B8FA-A8D5372F38E8\t2026-10-21 15:22:36 +0000\t/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit\n (1 plug-in)\n"
|
||||
},
|
||||
"failure": {
|
||||
"type": "KeyboardInterrupt",
|
||||
"message": ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
com.dayu.beatygril.fxplug(1.1) DDFB35E6-8D3D-4436-B8FA-A8D5372F38E8 2026-10-21 15:22:36 +0000 /Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit
|
||||
(1 plug-in)
|
||||
@@ -0,0 +1,24 @@
|
||||
Executable=/Library/Plug-Ins/FxPlug/beatygril.app/Contents/MacOS/beatygril
|
||||
Identifier=com.dayu.beatygril
|
||||
Format=app bundle with Mach-O universal (x86_64 arm64)
|
||||
CodeDirectory v=20500 size=478 flags=0x10000(runtime) hashes=4+7 location=embedded
|
||||
Hash type=sha256 size=32
|
||||
CandidateCDHash sha256=3b65dadb35fe03201c99ce39fb1a41bb3ed6b00a
|
||||
CandidateCDHashFull sha256=3b65dadb35fe03201c99ce39fb1a41bb3ed6b00a3b65e0bd8d653434f77cad3a
|
||||
Hash choices=sha256
|
||||
CMSDigest=3b65dadb35fe03201c99ce39fb1a41bb3ed6b00a3b65e0bd8d653434f77cad3a
|
||||
CMSDigestType=2
|
||||
CDHash=3b65dadb35fe03201c99ce39fb1a41bb3ed6b00a
|
||||
Signature size=8968
|
||||
Authority=Developer ID Application: zhong luo (B3W25FX5H5)
|
||||
Authority=Developer ID Certification Authority
|
||||
Authority=Apple Root CA
|
||||
Timestamp=Oct 21, 2026 at 23:22:09
|
||||
Notarization Ticket=stapled
|
||||
Info.plist entries=22
|
||||
TeamIdentifier=B3W25FX5H5
|
||||
Runtime Version=27.0.0
|
||||
Sealed Resources version=2 rules=13 files=4
|
||||
Internal requirements count=1 size=180
|
||||
Total signatures=1
|
||||
Chosen signature=1
|
||||
@@ -0,0 +1 @@
|
||||
{"message":"Processing complete","status":"Accepted","id":"a6218c05-b08f-417f-9617-c8e39b458a28"}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"installedVersionRetained": "1.1.1 (10)",
|
||||
"reason": "API handshake confirmed native; broader UI and new short/jaw fixes continue in next build; not tagged as final release",
|
||||
"removedKnownGeneratedPaths": [
|
||||
"/Users/dayu/Documents/PaintFCP/build/delivery/previous-beatygril.app",
|
||||
"/tmp/paintfcp-build-20261021/Build/Products/Release/beatygril.app",
|
||||
"/tmp/paintfcp-build-20261021",
|
||||
"/Users/dayu/Documents/PaintFCP/build/delivery/notarization.zip"
|
||||
],
|
||||
"UIOperatedByCleanup": false
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
Timestamp Ty Process[PID:TID]
|
||||
2026-10-21 23:24:07.867 Df beatygril XPC Service[22755:570e44] PGInspector connection readBefore=0 writeBefore=0 readInAction=1 writeInAction=1 viewSender=1 onMain=1
|
||||
2026-10-21 23:26:03.703 Df beatygril XPC Service[22755:570e44] PGInspector connection readBefore=0 writeBefore=0 readInAction=1 writeInAction=1 viewSender=1 onMain=1
|
||||
2026-10-21 23:29:21.518 Df beatygril XPC Service[22755:570e44] PGInspector connection readBefore=0 writeBefore=0 readInAction=1 writeInAction=1 viewSender=1 onMain=1
|
||||
2026-10-21 23:29:43.237 Df beatygril XPC Service[22755:570e44] PGInspector connection readBefore=0 writeBefore=0 readInAction=1 writeInAction=1 viewSender=1 onMain=1
|
||||
2026-10-21 23:33:38.371 Df beatygril XPC Service[22755:570e44] PGInspector connection readBefore=0 writeBefore=0 readInAction=1 writeInAction=1 viewSender=1 onMain=1
|
||||
2026-10-21 23:37:47.022 Df beatygril XPC Service[22755:570e44] PGInspector connection readBefore=0 writeBefore=0 readInAction=1 writeInAction=1 viewSender=1 onMain=1
|
||||
2026-10-21 23:38:06.329 Df beatygril XPC Service[22755:570e44] PGInspector connection readBefore=0 writeBefore=0 readInAction=1 writeInAction=1 viewSender=1 onMain=1
|
||||
@@ -0,0 +1,21 @@
|
||||
2026-10-21 23:20:17.783 osc-tests[22026:5696635] PGFaceOSC point-write desired=(0.25,0.75) setter=(0.25,0.75) readback=(0.25,0.75) mode=normalized verified=1
|
||||
2026-10-21 23:20:17.791 osc-tests[22026:5696635] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(80,180) surface=320x240 origin=0
|
||||
2026-10-21 23:20:18.509 osc-tests[22026:5696635] PGFaceOSC point-write desired=(0.8,0.3) setter=(1152,-432) readback=(0.8,0.3) mode=centered-pixels verified=1
|
||||
2026-10-21 23:20:18.509 osc-tests[22026:5696635] PGFaceOSC coordinates selection=(0.7,0.4) objectBounds=(-1920,-1080,3840,2160) canvas=(152,78) surface=320x240 origin=0
|
||||
2026-10-21 23:20:18.607 osc-tests[22026:5696635] PGFaceOSC point-write desired=(0.8,0.3) setter=(0.8,0.3) readback=(0.12345,0.87654) mode=normalized verified=0
|
||||
2026-10-21 23:20:18.607 osc-tests[22026:5696635] PGFaceOSC point-write desired=(0.75,0.25) setter=(0.75,0.25) readback=(0.75,0.25) mode=normalized verified=1
|
||||
2026-10-21 23:20:18.607 osc-tests[22026:5696635] PGFaceOSC coordinates selection=(0.75,0.25) objectBounds=(-100,-50,200,100) canvas=(160,60) surface=320x240 origin=0
|
||||
2026-10-21 23:20:18.684 osc-tests[22026:5696635] PGFaceOSC point-write desired=(0.625,0.375) setter=(0.625,0.375) readback=(0.625,0.375) mode=normalized verified=1
|
||||
2026-10-21 23:20:18.684 osc-tests[22026:5696635] PGFaceOSC coordinates selection=(0.625,0.375) objectBounds=(-100,-50,200,100) canvas=(240,60) surface=320x240 origin=0
|
||||
2026-10-21 23:20:18.765 osc-tests[22026:5696635] PGFaceOSC point-write desired=(0.25,0.75) setter=(0.25,0.75) readback=(0.25,0.75) mode=normalized verified=1
|
||||
2026-10-21 23:20:18.765 osc-tests[22026:5696635] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(144,180) surface=320x240 origin=0
|
||||
2026-10-21 23:20:18.864 osc-tests[22026:5696635] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
|
||||
2026-10-21 23:20:18.864 osc-tests[22026:5696635] PGFaceOSC coordinates selection=(0,1) objectBounds=(-100,-50,200,100) canvas=(100,140) surface=320x240 origin=0
|
||||
2026-10-21 23:20:18.947 osc-tests[22026:5696635] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(nan,nan) surface=320x240 origin=0
|
||||
2026-10-21 23:20:19.044 osc-tests[22026:5696635] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
|
||||
2026-10-21 23:20:19.044 osc-tests[22026:5696635] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(inf,nan) surface=320x240 origin=0
|
||||
2026-10-21 23:20:19.123 osc-tests[22026:5696635] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
|
||||
2026-10-21 23:20:19.123 osc-tests[22026:5696635] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(80,nan) surface=320x240 origin=0
|
||||
2026-10-21 23:20:19.203 osc-tests[22026:5696635] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
|
||||
2026-10-21 23:20:19.203 osc-tests[22026:5696635] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
|
||||
OSC adapter: 98 checks, 0 failures
|
||||
文件差异过大,未显示。
加载差异
@@ -0,0 +1,13 @@
|
||||
Target: 53ce88dd15983e9397ab08f5ec9ad608fce5d0b5
|
||||
Remote: https://dayu.media/git/dayu/paintfcp-fxplug.git
|
||||
Passed: True
|
||||
Tracked files: 125
|
||||
All tracked bytes/blob IDs/modes: True
|
||||
Git fsck --strict --full: 0
|
||||
LFS/submodules: [] / []
|
||||
Inspector tests: {'checks': 141, 'failures': 0, 'ASan': True, 'exitCode': 0, 'log': 'inspector-tests-asan.txt'}
|
||||
Template audit: structure_verified_native_loading_pending
|
||||
Core 559 tests not repeated; no APP build/host/UI/sign/install.
|
||||
Temporary clone removed: True
|
||||
Focused handshake review: PASS. startAction line159 precedes actual availability gate line163; pre-action API queries are diagnostics only. View init assigns its own NSView as actionSender; same sender balances endAction.
|
||||
Offscreen AppKit controls and action-gated/requires-view host mock covered; native Motion/FCP acceptance remains outside this task.
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"version": "1.1.2",
|
||||
"build": "13",
|
||||
"total": 1551,
|
||||
"counts": {
|
||||
"beauty": 49,
|
||||
"face": 116,
|
||||
"thinChinJawShort": 796,
|
||||
"hostAdapterASan": 335,
|
||||
"oscASan": 98,
|
||||
"inspectorASan": 157
|
||||
},
|
||||
"allPassed": true,
|
||||
"extraRealFramesASan": {
|
||||
"checks": 886,
|
||||
"includedRepeatedBaseChecks": true,
|
||||
"addedToTotal": false
|
||||
},
|
||||
"originCombinations": 28,
|
||||
"originTagsExactlyByteIdentical": true,
|
||||
"maxByteDifferenceVsIndependentUprightReference": 2,
|
||||
"meanByteDifferenceLimit": 0.005,
|
||||
"nativeCombinedBuildQA": "pending",
|
||||
"continuousPreview": "each changed step completes its own host action before mouse-up; mock publishes at endAction"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
FAIL: every drag step publishes a completed host snapshot before mouse-up
|
||||
FAIL: every drag step publishes a completed host snapshot before mouse-up
|
||||
FAIL: every drag step publishes a completed host snapshot before mouse-up
|
||||
Inspector: 155 checks, 3 failures
|
||||
@@ -0,0 +1,5 @@
|
||||
Beauty renderer: 49 checks, 0 failures (Metal).
|
||||
2026-10-21 23:46:40.676 face-tests[24719:5743987] FBBA: creating VNFaceBBoxAligner from VNFaceDetectorRevision2: VNFaceDetectorRevision2
|
||||
Face processing: 116 checks passed
|
||||
2026-10-21 23:46:52.402 slim-tests[24738:5744142] FBBA: creating VNFaceBBoxAligner from VNFaceDetectorRevision2: VNFaceDetectorRevision2
|
||||
PASS: 796 slim alignment checks; maximum UV oracle error 1.09828022e-05
|
||||
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"status": "complete",
|
||||
"bundleIdentifier": "com.dayu.beatygril",
|
||||
"installationPath": "/Library/Plug-Ins/FxPlug/beatygril.app",
|
||||
"options": {
|
||||
"waitForHosts": true,
|
||||
"userTests": false
|
||||
},
|
||||
"nativeEffectQA": {
|
||||
"status": "operator_confirmed",
|
||||
"performedByScript": false,
|
||||
"scopeDocument": "native-qa.json",
|
||||
"heldMouseCanvasTiming": "User confirmed continuous visible updates while dragging on 2026-10-22 (Asia/Shanghai); host not specified, no per-event frame timing claim.",
|
||||
"userConfirmation": {
|
||||
"date": "2026-10-22",
|
||||
"timezone": "Asia/Shanghai",
|
||||
"response": "拖动中已持续更新",
|
||||
"version": "1.1.2",
|
||||
"build": "13"
|
||||
}
|
||||
},
|
||||
"source": {
|
||||
"commit": "a2e33e8e00a99b95dc29d67a819cb0897eda5756",
|
||||
"trackedWorktreeChanges": []
|
||||
},
|
||||
"updatedAt": "2026-10-21T16:31:12.570976+00:00",
|
||||
"version": "1.1.2",
|
||||
"build": "13",
|
||||
"stagingSignature": "Executable=/private/tmp/paintfcp-build-20261021/Build/Products/Release/beatygril.app/Contents/MacOS/beatygril\nIdentifier=com.dayu.beatygril\nFormat=app bundle with Mach-O universal (x86_64 arm64)\nCodeDirectory v=20500 size=478 flags=0x10000(runtime) hashes=4+7 location=embedded\nHash type=sha256 size=32\nCandidateCDHash sha256=78b01963732b743659b501c5beedc96a725f1156\nCandidateCDHashFull sha256=78b01963732b743659b501c5beedc96a725f11560ebafe992a93195b3bae4dc2\nHash choices=sha256\nCMSDigest=78b01963732b743659b501c5beedc96a725f11560ebafe992a93195b3bae4dc2\nCMSDigestType=2\nCDHash=78b01963732b743659b501c5beedc96a725f1156\nSignature size=8968\nAuthority=Developer ID Application: zhong luo (B3W25FX5H5)\nAuthority=Developer ID Certification Authority\nAuthority=Apple Root CA\nTimestamp=Oct 22, 2026 at 00:20:39\nInfo.plist entries=22\nTeamIdentifier=B3W25FX5H5\nRuntime Version=27.0.0\nSealed Resources version=2 rules=13 files=4\nInternal requirements count=1 size=180\nTotal signatures=1\nChosen signature=1\n",
|
||||
"notarization": {
|
||||
"message": "Processing complete",
|
||||
"id": "5f5ce378-8430-467b-ae97-048e6bc81573",
|
||||
"status": "Accepted"
|
||||
},
|
||||
"signingVerified": true,
|
||||
"stapleValidated": true,
|
||||
"stagingPluginUnregistered": true,
|
||||
"runningHosts": [],
|
||||
"installedSignature": "Executable=/Library/Plug-Ins/FxPlug/beatygril.app/Contents/MacOS/beatygril\nIdentifier=com.dayu.beatygril\nFormat=app bundle with Mach-O universal (x86_64 arm64)\nCodeDirectory v=20500 size=478 flags=0x10000(runtime) hashes=4+7 location=embedded\nHash type=sha256 size=32\nCandidateCDHash sha256=78b01963732b743659b501c5beedc96a725f1156\nCandidateCDHashFull sha256=78b01963732b743659b501c5beedc96a725f11560ebafe992a93195b3bae4dc2\nHash choices=sha256\nCMSDigest=78b01963732b743659b501c5beedc96a725f11560ebafe992a93195b3bae4dc2\nCMSDigestType=2\nCDHash=78b01963732b743659b501c5beedc96a725f1156\nSignature size=8968\nAuthority=Developer ID Application: zhong luo (B3W25FX5H5)\nAuthority=Developer ID Certification Authority\nAuthority=Apple Root CA\nTimestamp=Oct 22, 2026 at 00:20:39\nNotarization Ticket=stapled\nInfo.plist entries=22\nTeamIdentifier=B3W25FX5H5\nRuntime Version=27.0.0\nSealed Resources version=2 rules=13 files=4\nInternal requirements count=1 size=180\nTotal signatures=1\nChosen signature=1\n",
|
||||
"installedSignatureVerified": true,
|
||||
"installedStapleValidated": true,
|
||||
"gatekeeperAccepted": true,
|
||||
"pluginDiscovery": {
|
||||
"bundleIdentifier": "com.dayu.beatygril.fxplug",
|
||||
"formalPath": "/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit",
|
||||
"output": " com.dayu.beatygril.fxplug(1.1)\t2293B0A5-4D98-4AE9-88F7-7022E3485E14\t2026-10-21 16:21:03 +0000\t/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit\n (1 plug-in)\n"
|
||||
},
|
||||
"removedGeneratedPaths": [
|
||||
"/Users/dayu/Documents/PaintFCP/build/delivery/previous-beatygril.app",
|
||||
"/tmp/paintfcp-build-20261021",
|
||||
"/Users/dayu/Documents/PaintFCP/build/delivery/notarization.zip"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"remote": "https://dayu.media/git/dayu/paintfcp-fxplug.git",
|
||||
"tag": "v1.1.2",
|
||||
"sourceCommit": "a2e33e8e00a99b95dc29d67a819cb0897eda5756",
|
||||
"passed": true,
|
||||
"testsNotRepeated": "Source is byte-identical to the independently recovered and Inspector-ASan-tested signed source node.",
|
||||
"tagCommit": "e9fddda5e13042f761755762fa39dfe090853c6a",
|
||||
"productSourceIdenticalToSignedNode": true,
|
||||
"trackedFiles": 158,
|
||||
"allBytesBlobsModesMatch": true,
|
||||
"gitFsck": "",
|
||||
"lfs": false,
|
||||
"submodules": false,
|
||||
"temporaryCloneRemoved": true,
|
||||
"completedAt": "2026-10-21T16:32:45.814667+00:00"
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
2026-10-21 23:54:44.774 host-tests[25302:5759794] FBBA: creating VNFaceBBoxAligner from VNFaceDetectorRevision2: VNFaceDetectorRevision2
|
||||
Default IOSurface maximum byte difference: 0
|
||||
Alternate destination tag maximum byte difference: 0
|
||||
Anatomy default 0/0 max=0 mean=0.0000000 eye=0/0 chin=0 inverted=0
|
||||
Anatomy default 0/1 max=0 mean=0.0000000 eye=0/0 chin=0 inverted=0
|
||||
Anatomy default 1/0 max=0 mean=0.0000000 eye=0/0 chin=0 inverted=0
|
||||
Anatomy default 1/1 max=0 mean=0.0000000 eye=0/0 chin=0 inverted=0
|
||||
Anatomy big-eye 0/0 max=2 mean=0.0008917 eye=33510/33880 chin=0 inverted=0
|
||||
Anatomy big-eye 0/1 max=2 mean=0.0008917 eye=33510/33880 chin=0 inverted=0
|
||||
Anatomy big-eye 1/0 max=2 mean=0.0008917 eye=33510/33880 chin=0 inverted=0
|
||||
Anatomy big-eye 1/1 max=2 mean=0.0008917 eye=33510/33880 chin=0 inverted=0
|
||||
Anatomy chin-positive 0/0 max=1 mean=0.0001459 eye=0/0 chin=116812 inverted=0
|
||||
Anatomy chin-positive 0/1 max=1 mean=0.0001459 eye=0/0 chin=116812 inverted=0
|
||||
Anatomy chin-positive 1/0 max=1 mean=0.0001459 eye=0/0 chin=116812 inverted=0
|
||||
Anatomy chin-positive 1/1 max=1 mean=0.0001459 eye=0/0 chin=116812 inverted=0
|
||||
Anatomy chin-negative 0/0 max=1 mean=0.0000849 eye=0/0 chin=60426 inverted=0
|
||||
Anatomy chin-negative 0/1 max=1 mean=0.0000849 eye=0/0 chin=60426 inverted=0
|
||||
Anatomy chin-negative 1/0 max=1 mean=0.0000849 eye=0/0 chin=60426 inverted=0
|
||||
Anatomy chin-negative 1/1 max=1 mean=0.0000849 eye=0/0 chin=60426 inverted=0
|
||||
Anatomy short-positive 0/0 max=2 mean=0.0011072 eye=0/0 chin=285953 inverted=0
|
||||
Anatomy short-positive 0/1 max=2 mean=0.0011072 eye=0/0 chin=285953 inverted=0
|
||||
Anatomy short-positive 1/0 max=2 mean=0.0011072 eye=0/0 chin=285953 inverted=0
|
||||
Anatomy short-positive 1/1 max=2 mean=0.0011072 eye=0/0 chin=285953 inverted=0
|
||||
Anatomy short-negative 0/0 max=2 mean=0.0008583 eye=0/0 chin=225648 inverted=0
|
||||
Anatomy short-negative 0/1 max=2 mean=0.0008583 eye=0/0 chin=225648 inverted=0
|
||||
Anatomy short-negative 1/0 max=2 mean=0.0008583 eye=0/0 chin=225648 inverted=0
|
||||
Anatomy short-negative 1/1 max=2 mean=0.0008583 eye=0/0 chin=225648 inverted=0
|
||||
Anatomy jaw 0/0 max=1 mean=0.0001974 eye=0/0 chin=12951 inverted=0
|
||||
Anatomy jaw 0/1 max=1 mean=0.0001974 eye=0/0 chin=12951 inverted=0
|
||||
Anatomy jaw 1/0 max=1 mean=0.0001974 eye=0/0 chin=12951 inverted=0
|
||||
Anatomy jaw 1/1 max=1 mean=0.0001974 eye=0/0 chin=12951 inverted=0
|
||||
Legacy fallback upright comparison: max=2 mean=0.0010357
|
||||
Host adapter: 335 checks, 0 failures
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"status": "passed",
|
||||
"productionSourceSHA256": "e4122ca95f7ba41bc925b4cc4bf2bf88a72044ea41880cd66acd9c8278a7daf4",
|
||||
"review": "Independent read-only review against recovered CPU constants and AIR: jaw six triples/radius/sequence, short polygon/axis/sign/thresholds, one landmark Y conversion, 56 argument layout, missing-topology pass-through. No blocking defect found.",
|
||||
"extraChecks": {
|
||||
"AIRProductCrossingCases": 30000,
|
||||
"axisYConversionIdentities": 1000
|
||||
},
|
||||
"scope": "Five remaining approximate shape controls and full combined-chain equivalence remain outside this repair. Sampled anatomical upper-contour/pupil points do not prove every temple pixel under all poses is unchanged. Native combined-build QA pending."
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
Inspector: 157 checks, 0 failures
|
||||
@@ -0,0 +1,2 @@
|
||||
com.dayu.beatygril.fxplug(1.1) 2293B0A5-4D98-4AE9-88F7-7022E3485E14 2026-10-21 16:21:03 +0000 /Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit
|
||||
(1 plug-in)
|
||||
@@ -0,0 +1,24 @@
|
||||
Executable=/Library/Plug-Ins/FxPlug/beatygril.app/Contents/MacOS/beatygril
|
||||
Identifier=com.dayu.beatygril
|
||||
Format=app bundle with Mach-O universal (x86_64 arm64)
|
||||
CodeDirectory v=20500 size=478 flags=0x10000(runtime) hashes=4+7 location=embedded
|
||||
Hash type=sha256 size=32
|
||||
CandidateCDHash sha256=78b01963732b743659b501c5beedc96a725f1156
|
||||
CandidateCDHashFull sha256=78b01963732b743659b501c5beedc96a725f11560ebafe992a93195b3bae4dc2
|
||||
Hash choices=sha256
|
||||
CMSDigest=78b01963732b743659b501c5beedc96a725f11560ebafe992a93195b3bae4dc2
|
||||
CMSDigestType=2
|
||||
CDHash=78b01963732b743659b501c5beedc96a725f1156
|
||||
Signature size=8968
|
||||
Authority=Developer ID Application: zhong luo (B3W25FX5H5)
|
||||
Authority=Developer ID Certification Authority
|
||||
Authority=Apple Root CA
|
||||
Timestamp=Oct 22, 2026 at 00:20:39
|
||||
Notarization Ticket=stapled
|
||||
Info.plist entries=22
|
||||
TeamIdentifier=B3W25FX5H5
|
||||
Runtime Version=27.0.0
|
||||
Sealed Resources version=2 rules=13 files=4
|
||||
Internal requirements count=1 size=180
|
||||
Total signatures=1
|
||||
Chosen signature=1
|
||||
@@ -0,0 +1,93 @@
|
||||
{
|
||||
"version": "1.1.2",
|
||||
"build": "13",
|
||||
"date": "2026-10-21T16:31:12.717850+00:00",
|
||||
"sourceCommit": "a2e33e8e00a99b95dc29d67a819cb0897eda5756",
|
||||
"installationPath": "/Library/Plug-Ins/FxPlug/beatygril.app",
|
||||
"codesign": {
|
||||
"exitCode": 0,
|
||||
"stdout": "",
|
||||
"stderr": ""
|
||||
},
|
||||
"staple": {
|
||||
"exitCode": 0,
|
||||
"stdout": "Processing: /Library/Plug-Ins/FxPlug/beatygril.app\nThe validate action worked!",
|
||||
"stderr": ""
|
||||
},
|
||||
"gatekeeper": {
|
||||
"exitCode": 0,
|
||||
"stdout": "",
|
||||
"stderr": "/Library/Plug-Ins/FxPlug/beatygril.app: accepted\nsource=Notarized Developer ID"
|
||||
},
|
||||
"identity": [
|
||||
{
|
||||
"path": "/Library/Plug-Ins/FxPlug/beatygril.app",
|
||||
"bundleIdentifier": "com.dayu.beatygril",
|
||||
"version": "1.1.2",
|
||||
"build": "13",
|
||||
"archs": "x86_64 arm64",
|
||||
"signature": "Executable=/Library/Plug-Ins/FxPlug/beatygril.app/Contents/MacOS/beatygril\nIdentifier=com.dayu.beatygril\nFormat=app bundle with Mach-O universal (x86_64 arm64)\nCodeDirectory v=20500 size=478 flags=0x10000(runtime) hashes=4+7 location=embedded\nHash type=sha256 size=32\nCandidateCDHash sha256=78b01963732b743659b501c5beedc96a725f1156\nCandidateCDHashFull sha256=78b01963732b743659b501c5beedc96a725f11560ebafe992a93195b3bae4dc2\nHash choices=sha256\nCMSDigest=78b01963732b743659b501c5beedc96a725f11560ebafe992a93195b3bae4dc2\nCMSDigestType=2\nCDHash=78b01963732b743659b501c5beedc96a725f1156\nSignature size=8968\nAuthority=Developer ID Application: zhong luo (B3W25FX5H5)\nAuthority=Developer ID Certification Authority\nAuthority=Apple Root CA\nTimestamp=Oct 22, 2026 at 00:20:39\nNotarization Ticket=stapled\nInfo.plist entries=22\nTeamIdentifier=B3W25FX5H5\nRuntime Version=27.0.0\nSealed Resources version=2 rules=13 files=4\nInternal requirements count=1 size=180\nTotal signatures=1\nChosen signature=1"
|
||||
},
|
||||
{
|
||||
"path": "/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit",
|
||||
"bundleIdentifier": "com.dayu.beatygril.fxplug",
|
||||
"version": "1.1.2",
|
||||
"build": "13",
|
||||
"archs": "x86_64 arm64",
|
||||
"signature": "Executable=/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit/Contents/MacOS/beatygril XPC Service\nIdentifier=com.dayu.beatygril.fxplug\nFormat=bundle with Mach-O universal (x86_64 arm64)\nCodeDirectory v=20500 size=869 flags=0x10000(runtime) hashes=20+3 location=embedded\nHash type=sha256 size=32\nCandidateCDHash sha256=79f2d1c3d638b27b2e5edeccf5d9255f6dd484b3\nCandidateCDHashFull sha256=79f2d1c3d638b27b2e5edeccf5d9255f6dd484b39910fe88ebdb2d09bd42bea4\nHash choices=sha256\nCMSDigest=79f2d1c3d638b27b2e5edeccf5d9255f6dd484b39910fe88ebdb2d09bd42bea4\nCMSDigestType=2\nCDHash=79f2d1c3d638b27b2e5edeccf5d9255f6dd484b3\nSignature size=8968\nAuthority=Developer ID Application: zhong luo (B3W25FX5H5)\nAuthority=Developer ID Certification Authority\nAuthority=Apple Root CA\nTimestamp=Oct 22, 2026 at 00:20:38\nInfo.plist entries=26\nTeamIdentifier=B3W25FX5H5\nRuntime Version=27.0.0\nSealed Resources version=2 rules=13 files=4\nInternal requirements count=1 size=188\nTotal signatures=1\nChosen signature=1"
|
||||
}
|
||||
],
|
||||
"template": [
|
||||
{
|
||||
"path": "/Users/dayu/Movies/Motion Templates.localized/Effects.localized/大鱼/大鱼人像美化/大鱼人像美化.moef",
|
||||
"bytes": 23073,
|
||||
"sha256": "e9fa97dc14323d0daa7d3d61b06a78ba4000a70ab9f5e67ebffd750d49df1e15"
|
||||
},
|
||||
{
|
||||
"path": "/Users/dayu/Movies/Motion Templates.localized/Effects.localized/大鱼/大鱼人像美化/Drop Zone.tiff",
|
||||
"bytes": 420128,
|
||||
"sha256": "a7f8e3a2a431dbb3ce9e777040bb55e701cfe9ab9632e8cbb10359732bc8692e"
|
||||
}
|
||||
],
|
||||
"discovery": {
|
||||
"exitCode": 0,
|
||||
"stdout": "com.dayu.beatygril.fxplug(1.1)\t2293B0A5-4D98-4AE9-88F7-7022E3485E14\t2026-10-21 16:21:03 +0000\t/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit\n (1 plug-in)",
|
||||
"stderr": ""
|
||||
},
|
||||
"duplicateInventory": {
|
||||
"scopes": [
|
||||
"/Users/dayu/Documents/PaintFCP",
|
||||
"/Users/dayu/.codex/worktrees",
|
||||
"/Users/dayu/Library/Developer/Xcode/DerivedData",
|
||||
"/Users/dayu/Desktop",
|
||||
"/Users/dayu/Downloads",
|
||||
"/Applications",
|
||||
"/Library/Plug-Ins/FxPlug",
|
||||
"/Users/dayu/Library/Plug-Ins/FxPlug",
|
||||
"/private/tmp/paintfcp-format-probe.PsxvYQ",
|
||||
"/private/tmp/paintfcp-y-review.KpPi8T",
|
||||
"/private/tmp/paintfcp-live-review.ItH4EG",
|
||||
"/private/tmp/paintfcp-slim-field.6fDdJj",
|
||||
"/private/tmp/paintfcp-chin-smoke.rfco16hq",
|
||||
"/private/tmp/paintfcp-rea.b3j9Te",
|
||||
"/private/tmp/paintfcp-slim-smoke.3ncdDe"
|
||||
],
|
||||
"matchingOuterApps": [
|
||||
{
|
||||
"path": "/Library/Plug-Ins/FxPlug/beatygril.app",
|
||||
"bundleIdentifier": "com.dayu.beatygril",
|
||||
"version": "1.1.2",
|
||||
"build": "13"
|
||||
}
|
||||
],
|
||||
"errors": [],
|
||||
"spotlight": {
|
||||
"exitCode": 0,
|
||||
"existingProductPaths": [
|
||||
"/Library/Plug-Ins/FxPlug/beatygril.app"
|
||||
]
|
||||
},
|
||||
"scopeLimit": "Bounded directories and existing indexed product paths; no full-volume completeness claim."
|
||||
},
|
||||
"nativeQAScope": "native-qa.json"
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"status": "failed",
|
||||
"bundleIdentifier": "com.dayu.beatygril",
|
||||
"installationPath": "/Library/Plug-Ins/FxPlug/beatygril.app",
|
||||
"options": {
|
||||
"waitForHosts": true,
|
||||
"userTests": false
|
||||
},
|
||||
"nativeEffectQA": {
|
||||
"status": "pending_operator",
|
||||
"performedByScript": false
|
||||
},
|
||||
"source": {
|
||||
"commit": "183bf0e4b2227477c4d38cb201ce2c0279eeb740",
|
||||
"trackedWorktreeChanges": []
|
||||
},
|
||||
"updatedAt": "2026-10-21T16:06:51.497292+00:00",
|
||||
"version": "1.1.2",
|
||||
"build": "11",
|
||||
"stagingSignature": "Executable=/private/tmp/paintfcp-build-20261021/Build/Products/Release/beatygril.app/Contents/MacOS/beatygril\nIdentifier=com.dayu.beatygril\nFormat=app bundle with Mach-O universal (x86_64 arm64)\nCodeDirectory v=20500 size=478 flags=0x10000(runtime) hashes=4+7 location=embedded\nHash type=sha256 size=32\nCandidateCDHash sha256=8e50a6a2b99f93880d8a5a8b5978d120a227a6b7\nCandidateCDHashFull sha256=8e50a6a2b99f93880d8a5a8b5978d120a227a6b72d1b6480bef338f2c8f1fbd4\nHash choices=sha256\nCMSDigest=8e50a6a2b99f93880d8a5a8b5978d120a227a6b72d1b6480bef338f2c8f1fbd4\nCMSDigestType=2\nCDHash=8e50a6a2b99f93880d8a5a8b5978d120a227a6b7\nSignature size=8969\nAuthority=Developer ID Application: zhong luo (B3W25FX5H5)\nAuthority=Developer ID Certification Authority\nAuthority=Apple Root CA\nTimestamp=Oct 21, 2026 at 23:57:21\nInfo.plist entries=22\nTeamIdentifier=B3W25FX5H5\nRuntime Version=27.0.0\nSealed Resources version=2 rules=13 files=4\nInternal requirements count=1 size=180\nTotal signatures=1\nChosen signature=1\n",
|
||||
"notarization": {
|
||||
"message": "Processing complete",
|
||||
"id": "a9b454c3-5d30-47bd-b266-f19447f3db7e",
|
||||
"status": "Accepted"
|
||||
},
|
||||
"signingVerified": true,
|
||||
"stapleValidated": true,
|
||||
"stagingPluginUnregistered": true,
|
||||
"runningHosts": [],
|
||||
"installedSignature": "Executable=/Library/Plug-Ins/FxPlug/beatygril.app/Contents/MacOS/beatygril\nIdentifier=com.dayu.beatygril\nFormat=app bundle with Mach-O universal (x86_64 arm64)\nCodeDirectory v=20500 size=478 flags=0x10000(runtime) hashes=4+7 location=embedded\nHash type=sha256 size=32\nCandidateCDHash sha256=8e50a6a2b99f93880d8a5a8b5978d120a227a6b7\nCandidateCDHashFull sha256=8e50a6a2b99f93880d8a5a8b5978d120a227a6b72d1b6480bef338f2c8f1fbd4\nHash choices=sha256\nCMSDigest=8e50a6a2b99f93880d8a5a8b5978d120a227a6b72d1b6480bef338f2c8f1fbd4\nCMSDigestType=2\nCDHash=8e50a6a2b99f93880d8a5a8b5978d120a227a6b7\nSignature size=8969\nAuthority=Developer ID Application: zhong luo (B3W25FX5H5)\nAuthority=Developer ID Certification Authority\nAuthority=Apple Root CA\nTimestamp=Oct 21, 2026 at 23:57:21\nNotarization Ticket=stapled\nInfo.plist entries=22\nTeamIdentifier=B3W25FX5H5\nRuntime Version=27.0.0\nSealed Resources version=2 rules=13 files=4\nInternal requirements count=1 size=180\nTotal signatures=1\nChosen signature=1\n",
|
||||
"installedSignatureVerified": true,
|
||||
"installedStapleValidated": true,
|
||||
"gatekeeperAccepted": true,
|
||||
"pluginDiscovery": {
|
||||
"bundleIdentifier": "com.dayu.beatygril.fxplug",
|
||||
"formalPath": "/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit",
|
||||
"output": " com.dayu.beatygril.fxplug(1.1)\tEFA7AB61-D5AA-44BB-B096-FF6BF12E7D02\t2026-10-21 15:57:43 +0000\t/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit\n (1 plug-in)\n"
|
||||
},
|
||||
"failure": {
|
||||
"type": "KeyboardInterrupt",
|
||||
"message": ""
|
||||
},
|
||||
"interimReason": "Native testing found number field focus-loss resubmission; combined build11 not accepted as final."
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"status": "failed",
|
||||
"bundleIdentifier": "com.dayu.beatygril",
|
||||
"installationPath": "/Library/Plug-Ins/FxPlug/beatygril.app",
|
||||
"options": {
|
||||
"waitForHosts": true,
|
||||
"userTests": false
|
||||
},
|
||||
"nativeEffectQA": {
|
||||
"status": "pending_operator",
|
||||
"performedByScript": false
|
||||
},
|
||||
"source": {
|
||||
"commit": "3f075922cbdd7b80b9609cb0640bc2591ad3240b",
|
||||
"trackedWorktreeChanges": []
|
||||
},
|
||||
"updatedAt": "2026-10-21T16:18:39.997054+00:00",
|
||||
"version": "1.1.2",
|
||||
"build": "12",
|
||||
"stagingSignature": "Executable=/private/tmp/paintfcp-build-20261021/Build/Products/Release/beatygril.app/Contents/MacOS/beatygril\nIdentifier=com.dayu.beatygril\nFormat=app bundle with Mach-O universal (x86_64 arm64)\nCodeDirectory v=20500 size=478 flags=0x10000(runtime) hashes=4+7 location=embedded\nHash type=sha256 size=32\nCandidateCDHash sha256=c50126c3762c66d9fcdc3890d3393ede57f75958\nCandidateCDHashFull sha256=c50126c3762c66d9fcdc3890d3393ede57f75958be276e1a4a83e1c11ed239b3\nHash choices=sha256\nCMSDigest=c50126c3762c66d9fcdc3890d3393ede57f75958be276e1a4a83e1c11ed239b3\nCMSDigestType=2\nCDHash=c50126c3762c66d9fcdc3890d3393ede57f75958\nSignature size=8969\nAuthority=Developer ID Application: zhong luo (B3W25FX5H5)\nAuthority=Developer ID Certification Authority\nAuthority=Apple Root CA\nTimestamp=Oct 22, 2026 at 00:14:13\nInfo.plist entries=22\nTeamIdentifier=B3W25FX5H5\nRuntime Version=27.0.0\nSealed Resources version=2 rules=13 files=4\nInternal requirements count=1 size=180\nTotal signatures=1\nChosen signature=1\n",
|
||||
"notarization": {
|
||||
"message": "Processing complete",
|
||||
"status": "Accepted",
|
||||
"id": "dbe3476f-c9d3-419d-9041-5180a37568dd"
|
||||
},
|
||||
"signingVerified": true,
|
||||
"stapleValidated": true,
|
||||
"stagingPluginUnregistered": true,
|
||||
"runningHosts": [],
|
||||
"installedSignature": "Executable=/Library/Plug-Ins/FxPlug/beatygril.app/Contents/MacOS/beatygril\nIdentifier=com.dayu.beatygril\nFormat=app bundle with Mach-O universal (x86_64 arm64)\nCodeDirectory v=20500 size=478 flags=0x10000(runtime) hashes=4+7 location=embedded\nHash type=sha256 size=32\nCandidateCDHash sha256=c50126c3762c66d9fcdc3890d3393ede57f75958\nCandidateCDHashFull sha256=c50126c3762c66d9fcdc3890d3393ede57f75958be276e1a4a83e1c11ed239b3\nHash choices=sha256\nCMSDigest=c50126c3762c66d9fcdc3890d3393ede57f75958be276e1a4a83e1c11ed239b3\nCMSDigestType=2\nCDHash=c50126c3762c66d9fcdc3890d3393ede57f75958\nSignature size=8969\nAuthority=Developer ID Application: zhong luo (B3W25FX5H5)\nAuthority=Developer ID Certification Authority\nAuthority=Apple Root CA\nTimestamp=Oct 22, 2026 at 00:14:13\nNotarization Ticket=stapled\nInfo.plist entries=22\nTeamIdentifier=B3W25FX5H5\nRuntime Version=27.0.0\nSealed Resources version=2 rules=13 files=4\nInternal requirements count=1 size=180\nTotal signatures=1\nChosen signature=1\n",
|
||||
"installedSignatureVerified": true,
|
||||
"installedStapleValidated": true,
|
||||
"gatekeeperAccepted": true,
|
||||
"pluginDiscovery": {
|
||||
"bundleIdentifier": "com.dayu.beatygril.fxplug",
|
||||
"formalPath": "/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit",
|
||||
"output": " com.dayu.beatygril.fxplug(1.1)\t51B2D2BF-2C90-41AA-BC26-50CFC1F824A9\t2026-10-21 16:14:42 +0000\t/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit\n (1 plug-in)\n"
|
||||
},
|
||||
"failure": {
|
||||
"type": "KeyboardInterrupt",
|
||||
"message": ""
|
||||
},
|
||||
"interimReason": "Geometry loads and reset changes slider; remote text editor remains focused and shows stale submitted text. Final dirty-aware refresh/abortEditing refinement pending build13."
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"version": "1.1.2",
|
||||
"build": "13",
|
||||
"sourceCommit": "a2e33e8e00a99b95dc29d67a819cb0897eda5756",
|
||||
"date": "2026-10-21T16:31:02.046442+00:00",
|
||||
"Motion": {
|
||||
"formalInstalledPluginLoaded": true,
|
||||
"numberInput": true,
|
||||
"singleResetUpdatesSliderAndText": true,
|
||||
"hostUndoUpdatesSliderAndText": true,
|
||||
"switchingEditedControlPreservesUndoneState": true,
|
||||
"saveCloseReopen": {
|
||||
"shortFace": 0.5,
|
||||
"jaw": 0
|
||||
},
|
||||
"earlierSameGeometryBuild11": "Isolated short +1/-1 and jaw +1; same-frame bypass is byte-identical to neutral viewer; profile and tilted-face images inspected. PGFaceProcessor bytes unchanged in final build13."
|
||||
},
|
||||
"FCP": {
|
||||
"formalInstalledPluginLoaded": true,
|
||||
"nativePanelEnabled": true,
|
||||
"numberInput": true,
|
||||
"singleResetUpdatesSliderAndText": true,
|
||||
"shapeIsolation": "Smoothing/color cards bypassed; other eight shape values cleared; short +1 and jaw +1 separately rendered on the same frame.",
|
||||
"viewerRespondedToEachIsolatedControl": true,
|
||||
"qaTimelineOnly": true
|
||||
},
|
||||
"continuousPreview": {
|
||||
"production": "Each changed continuous step finishes its FxPlug host action before mouse-up; repeated equal steps are deduplicated.",
|
||||
"automated": "Three intermediate snapshots published before gesture end; production view/controller exercised under action-scoped mock.",
|
||||
"nativePointerObservation": "User confirmed on 2026-10-22 (Asia/Shanghai): 拖动中已持续更新. Native held-drag visual behavior is accepted by the user; the host was not specified and per-event frame timing was not instrumented.",
|
||||
"userAcceptance": {
|
||||
"date": "2026-10-22",
|
||||
"timezone": "Asia/Shanghai",
|
||||
"version": "1.1.2",
|
||||
"build": "13",
|
||||
"response": "拖动中已持续更新",
|
||||
"passed": true,
|
||||
"scope": "User-observed continuous preview during held slider drag in at least one native host; does not establish separate held-drag acceptance in both Motion and FCP or a frame for every mouse event."
|
||||
}
|
||||
},
|
||||
"limits": [
|
||||
"No claim that every mouse event is presented as a separate host frame. Host scheduling and render speed can coalesce rapid updates.",
|
||||
"Remaining five approximate shape algorithms and full combined-chain pixel equivalence remain outside this repair.",
|
||||
"Some legacy Motion QA documents retain serialized standard parameter sections; new registration and current FCP template hide them.",
|
||||
"Native VoiceOver, old independent keyframe-curve migration, and exhaustive multi-face/all-pose testing not performed."
|
||||
],
|
||||
"privateEvidence": "User video, native screenshots, full AX and pixel-coordinate comparisons remain in ignored build/native-112; not uploaded."
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"message":"Processing complete","id":"5f5ce378-8430-467b-ae97-048e6bc81573","status":"Accepted"}
|
||||
@@ -0,0 +1,21 @@
|
||||
2026-10-21 23:54:43.574 osc-tests[25289:5759689] PGFaceOSC point-write desired=(0.25,0.75) setter=(0.25,0.75) readback=(0.25,0.75) mode=normalized verified=1
|
||||
2026-10-21 23:54:43.583 osc-tests[25289:5759689] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(80,180) surface=320x240 origin=0
|
||||
2026-10-21 23:54:44.244 osc-tests[25289:5759689] PGFaceOSC point-write desired=(0.8,0.3) setter=(1152,-432) readback=(0.8,0.3) mode=centered-pixels verified=1
|
||||
2026-10-21 23:54:44.244 osc-tests[25289:5759689] PGFaceOSC coordinates selection=(0.7,0.4) objectBounds=(-1920,-1080,3840,2160) canvas=(152,78) surface=320x240 origin=0
|
||||
2026-10-21 23:54:44.321 osc-tests[25289:5759689] PGFaceOSC point-write desired=(0.8,0.3) setter=(0.8,0.3) readback=(0.12345,0.87654) mode=normalized verified=0
|
||||
2026-10-21 23:54:44.321 osc-tests[25289:5759689] PGFaceOSC point-write desired=(0.75,0.25) setter=(0.75,0.25) readback=(0.75,0.25) mode=normalized verified=1
|
||||
2026-10-21 23:54:44.321 osc-tests[25289:5759689] PGFaceOSC coordinates selection=(0.75,0.25) objectBounds=(-100,-50,200,100) canvas=(160,60) surface=320x240 origin=0
|
||||
2026-10-21 23:54:44.402 osc-tests[25289:5759689] PGFaceOSC point-write desired=(0.625,0.375) setter=(0.625,0.375) readback=(0.625,0.375) mode=normalized verified=1
|
||||
2026-10-21 23:54:44.402 osc-tests[25289:5759689] PGFaceOSC coordinates selection=(0.625,0.375) objectBounds=(-100,-50,200,100) canvas=(240,60) surface=320x240 origin=0
|
||||
2026-10-21 23:54:44.483 osc-tests[25289:5759689] PGFaceOSC point-write desired=(0.25,0.75) setter=(0.25,0.75) readback=(0.25,0.75) mode=normalized verified=1
|
||||
2026-10-21 23:54:44.483 osc-tests[25289:5759689] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(144,180) surface=320x240 origin=0
|
||||
2026-10-21 23:54:44.561 osc-tests[25289:5759689] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
|
||||
2026-10-21 23:54:44.561 osc-tests[25289:5759689] PGFaceOSC coordinates selection=(0,1) objectBounds=(-100,-50,200,100) canvas=(100,140) surface=320x240 origin=0
|
||||
2026-10-21 23:54:44.646 osc-tests[25289:5759689] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(nan,nan) surface=320x240 origin=0
|
||||
2026-10-21 23:54:44.721 osc-tests[25289:5759689] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
|
||||
2026-10-21 23:54:44.721 osc-tests[25289:5759689] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(inf,nan) surface=320x240 origin=0
|
||||
2026-10-21 23:54:44.806 osc-tests[25289:5759689] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
|
||||
2026-10-21 23:54:44.807 osc-tests[25289:5759689] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(80,nan) surface=320x240 origin=0
|
||||
2026-10-21 23:54:44.889 osc-tests[25289:5759689] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
|
||||
2026-10-21 23:54:44.889 osc-tests[25289:5759689] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
|
||||
OSC adapter: 98 checks, 0 failures
|
||||
@@ -0,0 +1,2 @@
|
||||
2026-10-21 23:46:52.888 slim-tests-asan[24731:5744056] FBBA: creating VNFaceBBoxAligner from VNFaceDetectorRevision2: VNFaceDetectorRevision2
|
||||
PASS: 886 slim alignment checks; maximum UV oracle error 1.09828022e-05
|
||||
文件差异过大,未显示。
加载差异
@@ -0,0 +1,11 @@
|
||||
Target: a2e33e8e00a99b95dc29d67a819cb0897eda5756
|
||||
Remote: https://dayu.media/git/dayu/paintfcp-fxplug.git
|
||||
Passed: True
|
||||
Tracked files: 149
|
||||
All tracked bytes/blob IDs/modes: True
|
||||
Git fsck --strict --full: 0
|
||||
LFS/submodules: [] / []
|
||||
Inspector tests: {'checks': 157, 'failures': 0, 'ASan': True, 'exitCode': 0, 'log': 'inspector-tests-asan.txt'}
|
||||
Template audit: structure_verified_native_loading_pending
|
||||
Core 961 tests not repeated; no APP build/host/UI/sign/install.
|
||||
Temporary clone removed: True
|
||||
@@ -0,0 +1 @@
|
||||
Inspector: 157 checks, 0 failures
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"status": "complete",
|
||||
"bundleIdentifier": "com.dayu.beatygril",
|
||||
"installationPath": "/Library/Plug-Ins/FxPlug/beatygril.app",
|
||||
"options": {
|
||||
"waitForHosts": false,
|
||||
"userTests": false
|
||||
},
|
||||
"nativeEffectQA": {
|
||||
"status": "operator_confirmed",
|
||||
"performedByScript": false
|
||||
},
|
||||
"source": {
|
||||
"commit": "696d5f39b30404ed5d1d4c25f61d17475c7fc91b",
|
||||
"trackedWorktreeChanges": []
|
||||
},
|
||||
"updatedAt": "2026-10-21T17:39:12.166311+00:00",
|
||||
"version": "1.2.0",
|
||||
"build": "14",
|
||||
"stagingSignature": "Executable=/private/tmp/paintfcp-build-20261021/Build/Products/Release/beatygril.app/Contents/MacOS/beatygril\nIdentifier=com.dayu.beatygril\nFormat=app bundle with Mach-O universal (x86_64 arm64)\nCodeDirectory v=20500 size=478 flags=0x10000(runtime) hashes=4+7 location=embedded\nHash type=sha256 size=32\nCandidateCDHash sha256=c229cfbd5d26e1215ace1dd36e4442a412b52220\nCandidateCDHashFull sha256=c229cfbd5d26e1215ace1dd36e4442a412b52220f48215bd108107e1affb0b97\nHash choices=sha256\nCMSDigest=c229cfbd5d26e1215ace1dd36e4442a412b52220f48215bd108107e1affb0b97\nCMSDigestType=2\nCDHash=c229cfbd5d26e1215ace1dd36e4442a412b52220\nSignature size=8968\nAuthority=Developer ID Application: zhong luo (B3W25FX5H5)\nAuthority=Developer ID Certification Authority\nAuthority=Apple Root CA\nTimestamp=Oct 22, 2026 at 01:32:31\nInfo.plist entries=22\nTeamIdentifier=B3W25FX5H5\nRuntime Version=27.0.0\nSealed Resources version=2 rules=13 files=5\nInternal requirements count=1 size=180\nTotal signatures=1\nChosen signature=1\n",
|
||||
"notarization": {
|
||||
"status": "Accepted",
|
||||
"id": "3999fe91-a80e-49d3-9ed0-4003b82f6f54",
|
||||
"message": "Processing complete"
|
||||
},
|
||||
"signingVerified": true,
|
||||
"stapleValidated": true,
|
||||
"stagingPluginUnregistered": true,
|
||||
"runningHosts": [],
|
||||
"installedSignature": "Executable=/Library/Plug-Ins/FxPlug/beatygril.app/Contents/MacOS/beatygril\nIdentifier=com.dayu.beatygril\nFormat=app bundle with Mach-O universal (x86_64 arm64)\nCodeDirectory v=20500 size=478 flags=0x10000(runtime) hashes=4+7 location=embedded\nHash type=sha256 size=32\nCandidateCDHash sha256=c229cfbd5d26e1215ace1dd36e4442a412b52220\nCandidateCDHashFull sha256=c229cfbd5d26e1215ace1dd36e4442a412b52220f48215bd108107e1affb0b97\nHash choices=sha256\nCMSDigest=c229cfbd5d26e1215ace1dd36e4442a412b52220f48215bd108107e1affb0b97\nCMSDigestType=2\nCDHash=c229cfbd5d26e1215ace1dd36e4442a412b52220\nSignature size=8968\nAuthority=Developer ID Application: zhong luo (B3W25FX5H5)\nAuthority=Developer ID Certification Authority\nAuthority=Apple Root CA\nTimestamp=Oct 22, 2026 at 01:32:31\nNotarization Ticket=stapled\nInfo.plist entries=22\nTeamIdentifier=B3W25FX5H5\nRuntime Version=27.0.0\nSealed Resources version=2 rules=13 files=5\nInternal requirements count=1 size=180\nTotal signatures=1\nChosen signature=1\n",
|
||||
"installedSignatureVerified": true,
|
||||
"installedStapleValidated": true,
|
||||
"gatekeeperAccepted": true,
|
||||
"pluginDiscovery": {
|
||||
"bundleIdentifier": "com.dayu.beatygril.fxplug",
|
||||
"formalPath": "/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit",
|
||||
"output": " com.dayu.beatygril.fxplug(1.1)\tDECE2AF1-8D68-43B9-BE2E-6C97FB9EE96D\t2026-10-21 17:33:00 +0000\t/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit\n (1 plug-in)\n"
|
||||
},
|
||||
"removedGeneratedPaths": [
|
||||
"/Users/dayu/Documents/PaintFCP/build/delivery/previous-beatygril.app",
|
||||
"/tmp/paintfcp-build-20261021",
|
||||
"/Users/dayu/Documents/PaintFCP/build/delivery/notarization.zip"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
com.dayu.beatygril.fxplug(1.1) DECE2AF1-8D68-43B9-BE2E-6C97FB9EE96D 2026-10-21 17:33:00 +0000 /Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit
|
||||
(1 plug-in)
|
||||
@@ -0,0 +1,24 @@
|
||||
Executable=/Library/Plug-Ins/FxPlug/beatygril.app/Contents/MacOS/beatygril
|
||||
Identifier=com.dayu.beatygril
|
||||
Format=app bundle with Mach-O universal (x86_64 arm64)
|
||||
CodeDirectory v=20500 size=478 flags=0x10000(runtime) hashes=4+7 location=embedded
|
||||
Hash type=sha256 size=32
|
||||
CandidateCDHash sha256=c229cfbd5d26e1215ace1dd36e4442a412b52220
|
||||
CandidateCDHashFull sha256=c229cfbd5d26e1215ace1dd36e4442a412b52220f48215bd108107e1affb0b97
|
||||
Hash choices=sha256
|
||||
CMSDigest=c229cfbd5d26e1215ace1dd36e4442a412b52220f48215bd108107e1affb0b97
|
||||
CMSDigestType=2
|
||||
CDHash=c229cfbd5d26e1215ace1dd36e4442a412b52220
|
||||
Signature size=8968
|
||||
Authority=Developer ID Application: zhong luo (B3W25FX5H5)
|
||||
Authority=Developer ID Certification Authority
|
||||
Authority=Apple Root CA
|
||||
Timestamp=Oct 22, 2026 at 01:32:31
|
||||
Notarization Ticket=stapled
|
||||
Info.plist entries=22
|
||||
TeamIdentifier=B3W25FX5H5
|
||||
Runtime Version=27.0.0
|
||||
Sealed Resources version=2 rules=13 files=5
|
||||
Internal requirements count=1 size=180
|
||||
Total signatures=1
|
||||
Chosen signature=1
|
||||
@@ -0,0 +1,14 @@
|
||||
[
|
||||
{
|
||||
"elapsed": 13.717,
|
||||
"running": true,
|
||||
"processes": [
|
||||
"33709 /Library/Plug-Ins/FxPlug/beatygril.app/Contents/MacOS/beatygril"
|
||||
]
|
||||
},
|
||||
{
|
||||
"elapsed": 17.944,
|
||||
"running": false,
|
||||
"processes": []
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"version": "1.2.0",
|
||||
"build": "14",
|
||||
"source_commit": "696d5f39b30404ed5d1d4c25f61d17475c7fc91b",
|
||||
"native_app_card": "Observed correct classical information card, with accessible text",
|
||||
"exit_timer_seconds_after_window_shown": 3.0,
|
||||
"observed_process_lifetime_seconds": 4.227,
|
||||
"process_lifetime_note": "Includes process initialization before didFinishLaunching/window presentation. Not a measurement of timer drift.",
|
||||
"automatic_registration": "Own bundle registered via LSRegisterURL; installed PluginKit entry remains at unique formal path",
|
||||
"host_validation": {
|
||||
"FCP": "Fresh launch, new locked thumbnail visible in 大鱼磨皮瘦脸; applied ours to second clip in existing QA project; panel loaded, brightness set to20%, picture rendered without black output.",
|
||||
"Motion": "No fresh 1.2.0 UI test; renderer and inspector source unchanged from user-accepted 1.1.2."
|
||||
},
|
||||
"screenshots": "Native app and FCP screenshots kept local under ignored build/delivery; user stock imagery not published",
|
||||
"bounded_duplicate_inventory": "Final cleanup removed known build and backup app paths; final inventory follows in packaging verification."
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"status":"Accepted","id":"3999fe91-a80e-49d3-9ed0-4003b82f6f54","message":"Processing complete"}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "大鱼人像美化-1.2.0.pkg",
|
||||
"id": "96a3d1be-a751-4fb8-b062-33a09c01ddf2",
|
||||
"status": "Accepted",
|
||||
"createdDate": "2026-10-21T17:39:18.173Z"
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"package": "releases/1.2.0/大鱼人像美化-1.2.0.pkg",
|
||||
"bytes": 5011005,
|
||||
"sha256": "5bd77c28b3a7e54f3d4060be1f16bcb145d9bc4d6b9cfb9eb137c01130440243",
|
||||
"version": "1.2.0",
|
||||
"build": "14",
|
||||
"app_files_compared": 34,
|
||||
"app_byte_and_symlink_equality": true,
|
||||
"template_files": {
|
||||
"大鱼人像美化.moef": "e9fa97dc14323d0daa7d3d61b06a78ba4000a70ab9f5e67ebffd750d49df1e15",
|
||||
"Drop Zone.tiff": "a7f8e3a2a431dbb3ce9e777040bb55e701cfe9ab9632e8cbb10359732bc8692e",
|
||||
"large.png": "a02ddb255b1ae255c749bb837a759e2dee1c3b8ffb9bdc1167957761bd5a1b97",
|
||||
"small.png": "1552b838dd88f60dee0795e20aaca652f9c558ecefc93500e895fbff77c2a4d5"
|
||||
},
|
||||
"locked_relative_paths": [
|
||||
"大鱼人像美化/large.png",
|
||||
"大鱼人像美化/small.png"
|
||||
],
|
||||
"script_sha256": {
|
||||
"preinstall": "b0562f7705c84a2f34e31df67a4f0d429e60083bab122dad99f56a4b266c0174",
|
||||
"postinstall": "eeada4e07a284726d70772591c481f6ae12bfda12ed9ae3035d1089bca0bd59b"
|
||||
},
|
||||
"isolated_script_tests": [
|
||||
"fresh install",
|
||||
"replace old locked thumbnail",
|
||||
"partial copy failure restores old bytes and locks"
|
||||
],
|
||||
"isolation_changes": [
|
||||
"Console identity/home redirected to task fixture",
|
||||
"chown replaced with true",
|
||||
"FxPlug verification skipped; formal app signature and discovery verified separately"
|
||||
],
|
||||
"real_installer_execution": false,
|
||||
"app_and_template_installed": "APP installed through signed delivery workflow; user thumbnail directory updated separately, not by running this PKG",
|
||||
"fonts": "No editable third-party font dependency; promotional lettering baked into PNG. No optional font ZIP needed."
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"checked_commit": "906f770805fdbe8329c9d4ecd3594310e23bca6a",
|
||||
"reachable_blobs_scanned": 356,
|
||||
"secret_pattern_matches": [],
|
||||
"disallowed_extensions": [],
|
||||
"notes": [
|
||||
"No original competitor app, metallib, model or user stock video is included.",
|
||||
"NASA astronaut sample retains its source attribution.",
|
||||
"Tool-generated promotional art is labeled as illustration."
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# 1.2.0 / build 14
|
||||
|
||||
本次只修改外层启动器、版本和宣传资源;PGFaceProcessor、PGBeautyRenderer、PGInspectorView、PGInspectorState 与 Metal 渲染算法保持 1.1.2 用户验收版本。缩略图以用户指定的生成宣传画为来源,不冒充真实渲染静帧。
|
||||
|
||||
安装分类使用当前用户已移动后的 `Effects.localized/大鱼磨皮瘦脸/大鱼人像美化`。本地磁盘 moef 与源码基线 SHA-256 均为 e9fa97dc14323d0daa7d3d61b06a78ba4000a70ab9f5e67ebffd750d49df1e15,未覆盖任何最新手改。只为 large.png / small.png 设置 uchg。
|
||||
|
||||
待验证:正式 Release 构建、双架构、APP 签名 / 公证 / 票据 / 安装 / 插件发现、启动器三秒退出、PKG 签名公证与有效载荷 / 缩略图锁定脚本、远端发布与文章。
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"patch": "accepted",
|
||||
"id": 36,
|
||||
"private": false,
|
||||
"url": "https://dayu.media/git/dayu/paintfcp-fxplug",
|
||||
"anonymous_access": {
|
||||
"html_http": 302,
|
||||
"api_http": 401,
|
||||
"git_ls_remote": "credential request denied in anonymous mode",
|
||||
"reason": "Site-wide WordPress Git membership gateway is evaluated before Gitea public-repository permissions.",
|
||||
"server_code_changed": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"installationCategory": "大鱼磨皮瘦脸",
|
||||
"template": "大鱼人像美化.moef",
|
||||
"files": {
|
||||
"大鱼人像美化.moef": {
|
||||
"size": 23073,
|
||||
"sha256": "e9fa97dc14323d0daa7d3d61b06a78ba4000a70ab9f5e67ebffd750d49df1e15",
|
||||
"locked": false
|
||||
},
|
||||
"Drop Zone.tiff": {
|
||||
"size": 420128,
|
||||
"sha256": "a7f8e3a2a431dbb3ce9e777040bb55e701cfe9ab9632e8cbb10359732bc8692e",
|
||||
"locked": false
|
||||
},
|
||||
"large.png": {
|
||||
"size": 454742,
|
||||
"sha256": "a02ddb255b1ae255c749bb837a759e2dee1c3b8ffb9bdc1167957761bd5a1b97",
|
||||
"locked": true
|
||||
},
|
||||
"small.png": {
|
||||
"size": 45373,
|
||||
"sha256": "1552b838dd88f60dee0795e20aaca652f9c558ecefc93500e895fbff77c2a4d5",
|
||||
"locked": true
|
||||
}
|
||||
}
|
||||
}
|
||||
+50
@@ -81,3 +81,53 @@ Motion:独立普通视频项目,源PTS0、1920×1080/25fps/10秒,媒体通
|
||||
配套1.0.6效果模板及Drop Zone.tiff已在宿主退出时同步并逐字节核验;moef SHA-256为`30ca8edf36801264c5de2c753c40b720aba8052633620713a12401098fcd2409`。模板仍保留固定UUID及原有参数发布。旧未标记的跟踪数据会回退实时跟随;需要离线重放时请重新分析目标。
|
||||
|
||||
临时Release APP、旧APP备份和公证ZIP已清理。项目、Codex工作树、DerivedData、桌面、下载、Applications、系统/用户FxPlug目录及本次paintfcp临时目录扫描仅发现正式路径外层APP,扫描错误0;没有将这项有界扫描表述为所有卷的完整搜索。完整回执、签名、发现、模板摘要和扫描范围在`verification/v1.0.6/`。最终发布标签为`v1.0.6`,产品源码与签名构建节点ad54726一致;其后的提交仅更新交付文档、模板说明和验证回执。
|
||||
|
||||
## 1.1.0 原生卡片面板(构建前)
|
||||
|
||||
按参考界面的深色圆角卡片、青/绿/蓝紫分组、自绘滑块与开关、肤色圆点和单项重置实现独立AppKit面板,使用大鱼自己的品牌文字。新增自定义参数210及NSSecureCoding状态对象;FCP发布由48入口缩为面板+版权两项共3入口,旧49项参数和组路径保留隐藏兼容,手动分析命令隐藏且禁用。实时渲染不再载入任何保存的手动分析点阵,也不带旧分析大数据进入render snapshot。
|
||||
|
||||
完整1039项通过(美化49、人脸116、瘦脸/下巴394、宿主适配248、OSC98、检查器134),后三类适配/检查器使用ASan。检查器覆盖单项路由、完整安全归档、Motion自定义包内实际inner/outer的Foundation解码、初始不覆盖旧文档、数值插值、启停保值、重置、失效写入、事务配对、快速撤销/切时间后新编辑、显式数值输入与窄宽度布局。独立审查另用mock验证27项视图交互;280/320/380/520点宽度及更多展开不越界。测试修复了分组关闭后框仍显示、旧细节禁用状态丢失、快速撤销/换帧旧值回灌和旧显示缓存拦截明确输入等问题。
|
||||
|
||||
参考原版仅用于静态结构和外观分析,自有归档不含原版类/私有数据/品牌图。面板PNG为离屏演示参数预览,不是FCP原生截图。尚未操作Motion/FCP界面,真实宿主嵌入滚动、键盘/VoiceOver及工程重开由用户自测。源码与回执会按正式交付节点保存。
|
||||
|
||||
1.1.0(9)的签名构建源码为`f79d24eb0185f31feaae58b83b632f92dcb3ca91`,干净工作区构建。通用Release的Developer ID签名、严格验证、Apple公证Accepted(ce26be63-9dcd-436a-bcb5-bd294fcc92a9)和票据验证已通过。安装前曾检测到Motion/FCP仍运行,交付流程持锁等待用户自行保存退出;退出后完成下述替换。没有操作宿主界面。
|
||||
|
||||
独立远程克隆恢复已核对111个文件的字节、blob和模式、16项必要资源、strict/full fsck,无LFS/子模块;clone内134项Inspector ASan及模板审计通过,临时克隆已清理,详见`verification/v1.1.0/recovery-f79d24e.json`。
|
||||
|
||||
## 1.1.0 正式安装完成
|
||||
|
||||
检测时Motion/FCP已退出,交付流程完成1.1.0(9)替换。实际安装版及嵌套插件均为arm64/x86_64,签名团队B3W25FX5H5、可信时间戳、Hardened Runtime和固定Bundle ID正确;严格签名、Apple公证Accepted、粘附票据、Gatekeeper及PluginKit唯一正式路径通过。没有发送强退信号或操作宿主界面。
|
||||
|
||||
配套卡片版模板和Drop Zone.tiff已在宿主关闭时同步并逐字节核对,moef SHA256为`e1c0cff1dd3541ed97cab2cb39a76ad4f28dc659b3be7991cc7ac92dd8ea090b`。FCP发布入口为面板210及两项版权信息;旧参数定义隐藏保留,手动分析不再显示,输出保持实时自动跟随。自定义面板的真实嵌入、滚动、键盘/VoiceOver、保存重开及视觉均待用户自测。
|
||||
|
||||
临时APP、旧APP备份、公证ZIP已清理。项目、Codex工作树、DerivedData、桌面、下载、Applications、系统/用户FxPlug及paintfcp临时目录的有界扫描仅发现正式外层APP,扫描错误0;不将该范围扩张为全部挂载卷搜索。完整回执和扫描范围见`verification/v1.1.0/installed-verification.json`。最终恢复标签`v1.1.0`包含交付文档,签名产品源码保持f79d24eb0185f31feaae58b83b632f92dcb3ca91。
|
||||
|
||||
## 1.1.1 面板交互修复(原生验收进行中)
|
||||
|
||||
用户报告1.1.0界面无法交互并授权接手Motion/FCP测试。FCP中“大鱼人像美化验收-20261021”独立测试时间线复现:所有PG参数控件被禁用,页脚“等待宿主连接”;“更多设置”可展开,说明视图事件仍正常,故不是整张面板无法命中。失败AX与截图保留本机build/native-111,不上传用户测试画面或资源库内容。
|
||||
|
||||
连接代码原先在开启自定义参数action之前就要求读写API存在,且action sender传NSObject控制器;现先用真实NSView sender开启action,再在其上下文里取得API,缺API时配对结束。新增action-scoped模拟宿主及sender校验用例:修改前141项中5项失败,修改后全通过,包含临时接口失败后重新连接。接口前后布尔日志保留为原生诊断,根因的实际宿主确认仍待新版加载。
|
||||
|
||||
1.1.1(10)完整1046项回归通过(49美化、116人脸、394瘦脸/下巴、248宿主、98 OSC、141检查器),后三套启用ASan。正式编译、公证与原生Motion/FCP交互验收进行中,尚未把自动检查等同于原生通过。
|
||||
|
||||
1.1.1中间正式安装版在FCP原生确认API门控修复:read/writeBefore均0,action内均1,控件启用,重置和37%数值提交有效。原生日志与结论见`analysis/面板交互连接修复.md`。当前继续处理用户新报的短脸/下颌骨位置偏差,不将中间版标记为最终原生验收通过;临时APP、旧APP备份和公证ZIP已清理,保留唯一正式1.1.1等待合并修复。
|
||||
|
||||
## 1.1.2 短脸与下颌骨修复(构建前)
|
||||
|
||||
替换短脸和下颌骨的旧脸框近似,恢复目标76点的11边下半脸区域与六组局部采样,沿用单次图像重采样和图像/四张蒙版共用坐标场。四项已恢复控制缺完整76点时安全直通,其余五项和全链组合仍有近似边界。分析报告、CPU证据和AIR摘要见analysis/短脸与下颌骨分析.md。
|
||||
|
||||
1551项全部通过:49美化、116人脸、796瘦脸/下巴/短脸/下颌骨、335宿主适配ASan、98 OSC ASan、157检查器ASan。宿主独立物理行序用例扩展到短脸正负与下颌骨,28种控制/原点组合响应在预期下半脸,瞳孔区差异为0,坐标标签切换逐字节一致;独立upright参考最大差2/255,均差<0.005/255。额外真实正面/侧脸ASan专项886项通过,含重复基础用例,不相加。已核对生产源码与冻结摘要一致。合并版尚待正式构建和原生验收。
|
||||
|
||||
## 1.1.2(13)正式安装与原生验收
|
||||
|
||||
最终签名源码`a2e33e8e00a99b95dc29d67a819cb0897eda5756`。通用Release编译、Developer ID逐层签名、Apple公证Accepted(5f5ce378-8430-467b-ae97-048e6bc81573)、票据粘附/验证、安装身份、Gatekeeper及PluginKit正式路径通过。唯一正式APP、模板/TIFF和本次临时副本清理的范围与结果见verification/v1.1.2/installed-verification.json;扫描错误0,未扩张为全部挂载卷搜索。
|
||||
|
||||
构建前模块回归1551项通过:49美化、116人脸、796几何、335宿主适配ASan、98 OSC ASan、157 Inspector ASan。几何/宿主适配在对应未变化源码上验证,最后两轮只重复与界面修复有关的Inspector;额外真实帧886项包含重复基础用例,不相加。独立从远程恢复最终产品源码149文件和16资源,fsck及157项Inspector ASan通过,克隆已清理。
|
||||
|
||||
实际操作均在独立测试文档/时间线内:Motion最终版数值输入、重置同时更新slider/text、宿主撤销、切换输入不复活旧值、保存关闭重开(短脸0.5/下颌骨0)通过。相同几何源码在build11中已做单独短脸正负、下颌骨、停用同帧对照及侧脸/歪头图像检查;final build13继续沿用相同几何字节。FCP最终版原生面板启用、数值输入、单击重置同步显示、清零其他脸型后分别短脸1/下颌骨1的同帧画面响应通过。截图、完整AX和物理坐标差只留本机,不上传用户影像。
|
||||
|
||||
连续预览:生产NSSlider continuous保持开启,view不再把整个拖动包在同一次controller action里;每个有效步进独立结束宿主参数上下文。模拟宿主中三个中间状态在mouse-up前完成发布(旧实现3失败,修复后通过)。CUA坐标拖动未能可靠操纵显示中的滑杆,未记录到可信的原生按住期间排帧证据,已请求用户直接观察;此项保留待反馈,不写作原生连续预览通过。快速更新可能被宿主合并,各项其余五类近似算法和全部组合不宣称原版逐像素一致。详细范围在verification/v1.1.2/native-qa.json。
|
||||
|
||||
## 1.1.2 连续预览用户验收补充(2026-10-22)
|
||||
|
||||
用户明确回复“拖动中已持续更新”,确认最终1.1.2(13)按住滑杆拖动时画面能够持续更新,关闭上节待反馈项。此项证据来自用户原生实测;未指定具体宿主,不声称两个宿主分别完成按住拖动验收,也没有测量每个鼠标事件的独立排帧。本次仅更新验收记录,不改变产品源码、安装版或既有发布标签。
|
||||
@@ -0,0 +1,66 @@
|
||||
<!-- wp:paragraph -->
|
||||
<p>大鱼人像美化是一款适用于 Final Cut Pro 与 Motion 的免费开源人像插件,把磨皮、美白和脸型调整放进同一块原生卡片面板。插件会识别当前画面中的人脸并自动跟随,无需先点击分析;你可以按素材需要调整强度,拖动滑杆时持续查看效果。</p>
|
||||
<!-- /wp:paragraph -->
|
||||
<!-- wp:image {"sizeSlug":"full"} -->
|
||||
<figure class="wp-block-image size-full"><img src="{{media.cover.url}}" alt="大鱼人像美化古风宣传封面,展示自动瘦脸、自动跟随与自动美颜"/><figcaption>古风宣传插画;面部线条用于示意瘦脸与跟随功能。</figcaption></figure>
|
||||
<!-- /wp:image -->
|
||||
<!-- wp:heading -->
|
||||
<h2 class="wp-block-heading">可以做什么</h2>
|
||||
<!-- /wp:heading -->
|
||||
<!-- wp:list -->
|
||||
<ul class="wp-block-list"><li>磨皮:局部磨皮、全局磨皮与五官保护,强度分别控制。</li><li>美白:肤色预设、肤色亮度、皮肤色相和牙齿美白。</li><li>脸型:瘦脸、大眼、短脸、窄脸、下巴、V 脸、下颌骨、颧骨与小头调整。</li><li>自动跟随:默认跟随主要人脸,也提供多人模式、防跳脸范围与检测框预览。</li><li>实时预览:滑杆、数值、单项开关和重置集中在同一面板,便于反复对比。</li></ul>
|
||||
<!-- /wp:list -->
|
||||
<!-- wp:paragraph -->
|
||||
<p>自动识别负责定位处理区域,效果强度由你控制。建议先从较小的数值开始;多人交叉、严重遮挡和极端侧脸的结果需要结合具体素材检查。</p>
|
||||
<!-- /wp:paragraph -->
|
||||
<!-- wp:heading -->
|
||||
<h2 class="wp-block-heading">版本与下载</h2>
|
||||
<!-- /wp:heading -->
|
||||
<!-- wp:paragraph -->
|
||||
<p>当前版本 <strong>1.2.0(14)</strong>,支持 macOS 13 或更高版本,包含 Apple Silicon 与 Intel 两种架构。下载包包含 FxPlug4 程序和一个配套 FCP 效果模板。APP 与 PKG 均已完成 Developer ID 正式签名、Apple 公证和票据粘附;安装包为所有用户免费资源。</p>
|
||||
<!-- /wp:paragraph -->
|
||||
<!-- wp:paragraph -->
|
||||
<p>本次更新加入古风封面与锁定缩略图、免费开源说明页和主 APP 三秒自动退出。人脸处理与面板交互沿用 1.1.2 已验收实现,不需要重新运行手动分析。</p>
|
||||
<!-- /wp:paragraph -->
|
||||
<!-- wp:heading -->
|
||||
<h2 class="wp-block-heading">安装</h2>
|
||||
<!-- /wp:heading -->
|
||||
<!-- wp:paragraph -->
|
||||
<p>安装前先保存工作并退出 Final Cut Pro 和 Motion,安装期间保持关闭。运行本页提供的 PKG,按安装器提示完成安装;它会把程序安装到 <code>/Library/Plug-Ins/FxPlug/beatygril.app</code>,把配套效果安装到当前登录用户的 Motion Templates 目录,并自动验证和修复系统插件注册。</p>
|
||||
<!-- /wp:paragraph -->
|
||||
<!-- wp:paragraph -->
|
||||
<p>本插件免费开源,<strong>无需激活码、付费注册或账号授权</strong>。主 APP 打开时显示说明图,窗口显示三秒后自动退出;宿主中的滤镜由独立服务运行。安装器还会重新锁定两张缩略图,避免被宿主自动替换。</p>
|
||||
<!-- /wp:paragraph -->
|
||||
<!-- wp:image {"sizeSlug":"full"} -->
|
||||
<figure class="wp-block-image size-full"><img src="{{card_url}}" alt="主程序说明页:免费开源,自动注册,无需激活码,启动三秒后自动退出"/></figure>
|
||||
<!-- /wp:image -->
|
||||
<!-- wp:heading -->
|
||||
<h2 class="wp-block-heading">使用</h2>
|
||||
<!-- /wp:heading -->
|
||||
<!-- wp:paragraph -->
|
||||
<p>Final Cut Pro:打开<strong>效果</strong>浏览器,找到<strong>大鱼磨皮瘦脸</strong>分类,将<strong>大鱼人像美化</strong>添加到人像片段。Motion:在原生滤镜资源库的<strong>大鱼</strong>分类中选择<strong>大鱼人像美化</strong>。</p>
|
||||
<!-- /wp:paragraph -->
|
||||
<!-- wp:paragraph -->
|
||||
<p>在检查器中按需调节磨皮、美白和脸型。每项左侧的眼睛可暂时停用该项,数值会保留;右侧回转箭头用于重置。人脸会随画面自动更新,开启检测框预览可以检查跟随位置。多人模式建议先用短片段观察稳定性。</p>
|
||||
<!-- /wp:paragraph -->
|
||||
<!-- wp:heading -->
|
||||
<h2 class="wp-block-heading">常见问题</h2>
|
||||
<!-- /wp:heading -->
|
||||
<!-- wp:paragraph -->
|
||||
<p>如果效果没有出现或显示红色警告,先退出 Final Cut Pro 和 Motion,再打开一次主 APP,等待其自动退出,然后重新启动宿主。可在“终端”执行下面这条命令:</p>
|
||||
<!-- /wp:paragraph -->
|
||||
<!-- wp:code -->
|
||||
<pre class="wp-block-code"><code>open '/Library/Plug-Ins/FxPlug/beatygril.app'</code></pre>
|
||||
<!-- /wp:code -->
|
||||
<!-- wp:paragraph -->
|
||||
<p>该流程可重新触发系统注册,并不能解决所有报错。仍异常时请核对 macOS、宿主版本及安装包完整性;不要只复制单个 .moef 文件,配套程序与 Drop Zone.tiff 也需要保留。当前仅提供上述一个效果模板。</p>
|
||||
<!-- /wp:paragraph -->
|
||||
<!-- wp:heading -->
|
||||
<h2 class="wp-block-heading">源码与开源许可</h2>
|
||||
<!-- /wp:heading -->
|
||||
<!-- wp:paragraph -->
|
||||
<p>源码仓库:<a href="https://dayu.media/git/dayu/paintfcp-fxplug" target="_blank" rel="noreferrer noopener">大鱼人像美化 · paintfcp-fxplug</a>。仓库已设为公开属性;目前大鱼站的统一 Git 网关仍要求登录并校验会员。为方便所有读者获取源码,另外提供 <a href="{{source_url}}">1.2.0 源码快照 ZIP(免费、免 Git 登录)</a>,可直接下载研究和自行编译。</p>
|
||||
<!-- /wp:paragraph -->
|
||||
<!-- wp:paragraph -->
|
||||
<p>项目原创代码采用 MIT 许可,源码包含 Xcode 工程、Objective-C / Metal 实现、模板及构建与验证说明。Apple SDK、运行框架及样本按各自许可使用,详见源码中的 LICENSE 和 THIRD_PARTY_NOTICES.md。自行编译需要 Xcode 与 FxPlug SDK;证书和签名私钥不包含在源码中。</p>
|
||||
<!-- /wp:paragraph -->
|
||||
二进制文件未显示。
@@ -0,0 +1,219 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Encode/audit our own FxPlug custom value in observed Motion XML structure.
|
||||
|
||||
No host is started. A successful audit verifies structure and archive contents;
|
||||
it does not establish Motion/FCP custom-view loading or native interaction.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import base64
|
||||
import hashlib
|
||||
import json
|
||||
import math
|
||||
from pathlib import Path
|
||||
import plistlib
|
||||
import re
|
||||
import string
|
||||
import struct
|
||||
import sys
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
ALPHABET = "*-0123456789" + string.ascii_uppercase + string.ascii_lowercase
|
||||
STANDARD = string.ascii_uppercase + string.ascii_lowercase + string.digits + "+/"
|
||||
PLUGIN_UUID = "FB14B0E6-59B2-4836-916F-CD0C559E9241"
|
||||
PLACEHOLDER_SHA256 = "a7f8e3a2a431dbb3ce9e777040bb55e701cfe9ab9632e8cbb10359732bc8692e"
|
||||
|
||||
|
||||
def require(condition: bool, message: str) -> None:
|
||||
if not condition:
|
||||
raise ValueError(message)
|
||||
|
||||
|
||||
def digest(data: bytes) -> str:
|
||||
return hashlib.sha256(data).hexdigest()
|
||||
|
||||
|
||||
def resolve(objects: list, value, seen: frozenset[int] = frozenset()):
|
||||
"""Read Foundation collection values without instantiating archived classes."""
|
||||
if isinstance(value, plistlib.UID):
|
||||
index = value.data
|
||||
require(0 <= index < len(objects) and index not in seen, "Invalid/cyclic archive UID")
|
||||
return resolve(objects, objects[index], seen | {index})
|
||||
if isinstance(value, list):
|
||||
return [resolve(objects, item, seen) for item in value]
|
||||
if isinstance(value, dict):
|
||||
if "NS.keys" in value:
|
||||
keys, items = value["NS.keys"], value["NS.objects"]
|
||||
require(len(keys) == len(items), "Mismatched dictionary keys/objects")
|
||||
return {resolve(objects, k, seen): resolve(objects, v, seen) for k, v in zip(keys, items)}
|
||||
if "NS.objects" in value:
|
||||
return resolve(objects, value["NS.objects"], seen)
|
||||
return {k: resolve(objects, v, seen) for k, v in value.items() if k != "$class"}
|
||||
return value
|
||||
|
||||
|
||||
def inspect_inner(inner: bytes) -> dict:
|
||||
archive = plistlib.loads(inner)
|
||||
require(archive.get("$archiver") == "NSKeyedArchiver", "Inner value is not a keyed archive")
|
||||
require(set(archive.get("$top", {})) == {"Custom Data"}, "Inner archive requires the Custom Data key")
|
||||
objects = archive["$objects"]
|
||||
state = objects[archive["$top"]["Custom Data"].data]
|
||||
require(objects[state["$class"].data]["$classname"] == "PGInspectorState", "Unexpected custom class")
|
||||
classes = sorted({x["$classname"] for x in objects if isinstance(x, dict) and "$classname" in x})
|
||||
require(set(classes) <= {"PGInspectorState", "NSObject", "NSDictionary", "NSMutableDictionary", "NSSet", "NSMutableSet", "NSNumber"}, "Unexpected archived class")
|
||||
decoded = resolve(objects, state)
|
||||
require(decoded.get("schema") == 1 and isinstance(decoded.get("edited"), bool), "Unexpected state schema")
|
||||
values = decoded.get("values")
|
||||
require(isinstance(values, dict) and values, "Missing numeric control values")
|
||||
require(all(isinstance(k, int) and not isinstance(k, bool) and isinstance(v, (int, float)) and math.isfinite(v) for k, v in values.items()), "Non-numeric control values")
|
||||
disabled = decoded.get("disabled")
|
||||
require(isinstance(disabled, list) and all(isinstance(v, int) for v in disabled), "Invalid disabled controls")
|
||||
return {"class": "PGInspectorState", "schema": decoded["schema"], "hasEdits": decoded["edited"],
|
||||
"values": values, "disabled": disabled, "classes": classes,
|
||||
"innerBytes": len(inner), "innerSHA256": digest(inner)}
|
||||
|
||||
|
||||
def wrap_inner(inner: bytes) -> bytes:
|
||||
inspect_inner(inner)
|
||||
# This is the observed NSKeyedArchiver wrapper, containing NSData and
|
||||
# NSNumber(false), rather than a vendor custom object or copied blob.
|
||||
return plistlib.dumps({"$version": 100000, "$archiver": "NSKeyedArchiver",
|
||||
"$top": {"DataIsLegacy": plistlib.UID(2), "BlindDataObject": plistlib.UID(1)},
|
||||
"$objects": ["$null", inner, False]}, fmt=plistlib.FMT_BINARY, sort_keys=False)
|
||||
|
||||
|
||||
def encode_outer(outer: bytes) -> str:
|
||||
data = struct.pack(">Q", len(outer)) + b"*" + outer
|
||||
data += b"\0" * (-len(data) % 3)
|
||||
return base64.b64encode(data).decode("ascii").translate(str.maketrans(STANDARD, ALPHABET))
|
||||
|
||||
|
||||
def decode_text(text: str) -> tuple[bytes, bytes, dict]:
|
||||
require(text and len(text) % 4 == 0 and set(text) <= set(ALPHABET), "Invalid Motion text encoding")
|
||||
data = base64.b64decode(text.translate(str.maketrans(ALPHABET, STANDARD)), validate=True)
|
||||
require(len(data) >= 9 and data[8] == 0x2A, "Unexpected Motion envelope")
|
||||
length = int.from_bytes(data[:8], "big")
|
||||
require(0 < length <= 16 * 1024 * 1024 and 0 <= len(data) - 9 - length <= 2, "Invalid payload length")
|
||||
require(not any(data[9+length:]), "Nonzero envelope padding")
|
||||
outer = data[9:9+length]
|
||||
archive = plistlib.loads(outer)
|
||||
require(archive.get("$archiver") == "NSKeyedArchiver", "Unexpected outer archive")
|
||||
require(set(archive.get("$top", {})) == {"DataIsLegacy", "BlindDataObject"}, "Unexpected outer keys")
|
||||
objects = archive["$objects"]
|
||||
legacy = resolve(objects, archive["$top"]["DataIsLegacy"])
|
||||
inner = resolve(objects, archive["$top"]["BlindDataObject"])
|
||||
require(legacy is False and isinstance(inner, bytes), "Expected a nonlegacy NSData custom value")
|
||||
require(encode_outer(outer) == text, "Text roundtrip mismatch")
|
||||
state = inspect_inner(inner)
|
||||
state.update({"outerBytes": len(outer), "outerSHA256": digest(outer), "encodedCharacters": len(text), "exactTextRoundtrip": True})
|
||||
return outer, inner, state
|
||||
|
||||
|
||||
def find_plugin(root: ET.Element) -> ET.Element:
|
||||
matches = [x for x in root.iter("filter") if x.get("pluginUUID") == PLUGIN_UUID]
|
||||
require(len(matches) == 1, "Expected exactly one product filter")
|
||||
return matches[0]
|
||||
|
||||
|
||||
def parameter(node: ET.Element, channel: str) -> ET.Element:
|
||||
require(channel.startswith("./"), "Expected a relative parameter path")
|
||||
for identifier in channel[2:].split("/"):
|
||||
node = node.find(f"parameter[@id='{identifier}']")
|
||||
require(node is not None, f"Unresolved parameter path {channel}")
|
||||
return node
|
||||
|
||||
|
||||
def audit(path: Path, parameters_path: Path | None = None) -> tuple[dict, bytes, bytes]:
|
||||
root = ET.parse(path).getroot()
|
||||
plugin = find_plugin(root)
|
||||
custom = parameter(plugin, "./210")
|
||||
require(custom.get("name") == "" and custom.get("flags") == "12901679104", "Custom entry differs from observed native structure")
|
||||
require(custom.findtext("numberOfKeypoints") == "1" and custom.findtext("keypoint/time") == "0 0 0 0" and custom.find("keypoint/value") is not None, "Missing native custom keypoint skeleton")
|
||||
text = custom.findtext("dataValue")
|
||||
require(text == custom.findtext("defaultVal"), "Initial current/default custom values differ")
|
||||
outer, inner, state = decode_text(text)
|
||||
require(state["hasEdits"] is False, "Initial custom value must preserve legacy native settings")
|
||||
entities = [x for x in root.iter() if x.tag in {"scenenode", "layer", "group", "filter", "clip", "footage"}]
|
||||
ids = [x.get("id") for x in entities]
|
||||
require(None not in ids and len(ids) == len(set(ids)), "Duplicate/missing entity identity")
|
||||
by_id = dict(zip(ids, entities))
|
||||
targets = root.findall("./scene/publishSettings/target")
|
||||
require(len(targets) == 3, "Expected exactly three publications")
|
||||
require([x.get("name") for x in targets] == ["FCP 版本作者→", "更多模板浏览器访问→", ""], "Unexpected publication order/names")
|
||||
require(targets[2].get("object") == plugin.get("id") and targets[2].get("channel") == "./210", "Custom publication does not target this filter")
|
||||
for item in targets:
|
||||
require(item.get("object") in by_id, "Unresolved publication object")
|
||||
parameter(by_id[item.get("object")], item.get("channel"))
|
||||
for target, title in zip(targets[:2], ["大鱼先森Media", "dayu.media"]):
|
||||
entry = parameter(by_id[target.get("object")], "./2/100")
|
||||
require(len(entry.findall("entry")) == 1 and title in ET.tostring(entry, encoding="unicode"), "Copyright menu differs")
|
||||
require(parameter(plugin, "./100/12").get("value") == "0.5", "Template midtone default was lost")
|
||||
require(parameter(plugin, "./10005").get("value") == "0", "New template OSC must be off")
|
||||
for group in range(100, 106):
|
||||
parameter(plugin, f"./{group}")
|
||||
for identifier in (45, 46, 50, 51, 52, 53):
|
||||
parameter(plugin, f"./104/{identifier}")
|
||||
for identifier in (60, 61, 62):
|
||||
parameter(plugin, f"./{identifier}")
|
||||
metadata = parameters_path or path.with_name("PARAMETERS.json")
|
||||
listed = json.loads(metadata.read_text())
|
||||
for item in listed["parameters"]:
|
||||
parameter(plugin, item["channel"])
|
||||
require(bool(item["published"]) == (item["id"] == 210), "Legacy flat entry is still published")
|
||||
clips = root.findall(".//footage/clip")
|
||||
require(len(clips) == 1 and clips[0].findtext("pathURL") == "Drop Zone.tiff", "Unexpected effect source media")
|
||||
require(clips[0].get("id") == "510000006" and by_id["510000007"].find("clip") is clips[0], "Broken source clip/footage hierarchy")
|
||||
source = by_id["510000004"]
|
||||
require(parameter(source, "./1/324/300").get("value") == "510000006", "Broken Source Media reference")
|
||||
require(parameter(source, "./2/321").get("value") == "3", "Effect Source type changed")
|
||||
require(parameter(source, "./2/311").get("value") == "1", "Effect Source Drop Zone changed")
|
||||
media = path.parent / "Drop Zone.tiff"
|
||||
require(media.is_file() and digest(media.read_bytes()) == PLACEHOLDER_SHA256, "Placeholder missing/changed")
|
||||
result = {"status": "structure_verified_native_loading_pending", "templateSHA256": digest(path.read_bytes()),
|
||||
"pluginVersion": plugin.get("pluginVersion"), "publications": [dict(x.attrib) for x in targets],
|
||||
"parameterPathsResolved": len(listed["parameters"]), "sourceMediaClosed": True,
|
||||
"mediaSHA256": PLACEHOLDER_SHA256, "customState": state,
|
||||
"nativeLoading": "pending user; no host executed"}
|
||||
return result, outer, inner
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
sub = parser.add_subparsers(dest="command", required=True)
|
||||
enc = sub.add_parser("encode", help="Wrap our own secure Custom Data archive and encode Motion text")
|
||||
enc.add_argument("--inner", type=Path, required=True)
|
||||
enc.add_argument("--text-output", type=Path, required=True)
|
||||
enc.add_argument("--outer-output", type=Path)
|
||||
chk = sub.add_parser("audit", help="Read-only template/archive/reference checks; native loading stays pending")
|
||||
chk.add_argument("template", type=Path)
|
||||
chk.add_argument("--parameters", type=Path)
|
||||
chk.add_argument("--json-output", type=Path)
|
||||
chk.add_argument("--inner-output", type=Path)
|
||||
chk.add_argument("--outer-output", type=Path)
|
||||
args = parser.parse_args()
|
||||
if args.command == "encode":
|
||||
inner = args.inner.read_bytes(); outer = wrap_inner(inner); text = encode_outer(outer)
|
||||
decoded_outer, decoded_inner, result = decode_text(text)
|
||||
require(decoded_inner == inner and decoded_outer == outer, "Own archive roundtrip failed")
|
||||
args.text_output.write_text(text + "\n")
|
||||
if args.outer_output:
|
||||
args.outer_output.write_bytes(outer)
|
||||
else:
|
||||
result, outer, inner = audit(args.template, args.parameters)
|
||||
if args.json_output:
|
||||
args.json_output.write_text(json.dumps(result, ensure_ascii=False, indent=2) + "\n")
|
||||
if args.inner_output:
|
||||
args.inner_output.write_bytes(inner)
|
||||
if args.outer_output:
|
||||
args.outer_output.write_bytes(outer)
|
||||
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
sys.exit(main())
|
||||
except (ValueError, KeyError, IndexError, TypeError, OSError, ET.ParseError, plistlib.InvalidFileException) as error:
|
||||
print(f"FAIL: {error}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Publish this reviewed release through the connected Dayu article publisher.
|
||||
Secrets are read from macOS Keychain into memory; no auth material is persisted.
|
||||
"""
|
||||
from pathlib import Path
|
||||
import base64,hashlib,html,json,mimetypes,ssl,subprocess,urllib.error,urllib.parse,urllib.request,uuid
|
||||
ROOT=Path(__file__).resolve().parent.parent
|
||||
STATE=ROOT/'build/publish-operation.json';STATE.parent.mkdir(exist_ok=True)
|
||||
state=json.loads(STATE.read_text()) if STATE.exists() else {'operation':str(uuid.uuid4())}
|
||||
def save():STATE.write_text(json.dumps(state,ensure_ascii=False,indent=2)+'\n')
|
||||
save()
|
||||
secret=subprocess.run(['security','find-generic-password','-s','dayu-article-publisher','-a','dayu','-w'],text=True,capture_output=True,check=True).stdout.strip()
|
||||
auth='Basic '+base64.b64encode(('dayu:'+secret).encode()).decode()
|
||||
class NoRedirect(urllib.request.HTTPRedirectHandler):
|
||||
def redirect_request(self,*args,**kwargs):return None
|
||||
opener=urllib.request.build_opener(NoRedirect,urllib.request.HTTPSHandler(context=ssl.create_default_context(cafile='/etc/ssl/cert.pem')))
|
||||
def request(path,method='GET',body=None,headers=None):
|
||||
assert path.startswith('/wp-json/') and '\n' not in path
|
||||
h={'Authorization':auth,'Accept':'application/json'};h.update(headers or {})
|
||||
if isinstance(body,dict):body=json.dumps(body,ensure_ascii=False).encode();h['Content-Type']='application/json'
|
||||
req=urllib.request.Request('https://dayu.media'+path,data=body,headers=h,method=method)
|
||||
with opener.open(req,timeout=180) as f:return json.load(f)
|
||||
def upload(name,path,mime):
|
||||
if name in state:return state[name]
|
||||
filename=path.name
|
||||
matches=request('/wp/v2/media?search='+urllib.parse.quote(path.stem)+'&per_page=30&context=edit')
|
||||
for item in matches:
|
||||
if urllib.parse.unquote(item.get('source_url','')).endswith('/'+filename):
|
||||
# Only reuse a byte-identical previous upload.
|
||||
with urllib.request.urlopen(item['source_url'],context=ssl.create_default_context(cafile='/etc/ssl/cert.pem'),timeout=90) as r:data=r.read()
|
||||
if hashlib.sha256(data).digest()==hashlib.sha256(path.read_bytes()).digest():
|
||||
state[name]={'id':item['id'],'url':item['source_url']};save();return state[name]
|
||||
result=request('/wp/v2/media','POST',path.read_bytes(),{'Content-Type':mime,'Content-Disposition':"attachment; filename*=UTF-8''"+urllib.parse.quote(filename)})
|
||||
state[name]={'id':result['id'],'url':result['source_url']};save();return state[name]
|
||||
status=request('/dayu-publisher/v1/status');assert status['can_publish'] and status['can_upload'] and 'seo' in status['fields']
|
||||
source=upload('source',ROOT/'build/Dayu-Portrait-1.2.0-source.zip','application/zip')
|
||||
card=upload('card',ROOT/'beatygril/beatygril/Wrapper Application/Resources/LauncherInfo.png','image/png')
|
||||
content=(ROOT/'releases/1.2.0/article.html').read_text().replace('{{source_url}}',html.escape(source['url'],quote=True)).replace('{{card_url}}',html.escape(card['url'],quote=True))
|
||||
seo={
|
||||
'title':{'enabled':True,'value':'大鱼人像美化 1.2.0|FCP / Motion 免费开源自动美颜插件'},
|
||||
'description':{'enabled':True,'value':'大鱼人像美化是适用于 Final Cut Pro 和 Motion 的免费开源 FxPlug4 插件,支持磨皮、美白、自动人脸跟随及瘦脸调整。提供 Apple 签名公证 PKG 安装包和源码,安装自动注册,无需激活码,并附具体安装、使用及红色警告排查说明。'},
|
||||
'keywords':{'enabled':True,'value':'大鱼人像美化,FCP美颜插件,Motion磨皮,自动瘦脸,自动跟随,FxPlug4,免费开源'}
|
||||
}
|
||||
payload={'title':'大鱼人像美化 1.2.0:FCP / Motion 免费开源自动美颜插件','slug':'dayu-portrait-beauty','status':'publish','content':content,'excerpt':'磨皮、美白、瘦脸与自动人脸跟随,原生卡片面板实时预览。免费开源,自动注册,无需激活码;提供签名公证安装包和源码。','categories':[3],'tags':[], 'resource':{'cao_status':True,'cao_price':0,'cao_vip_rate':1,'cao_close_novip_pay':False,'cao_expire_day':0,'cao_pwd':'','cao_info':[{'title':'版本','desc':'1.2.0(14)'},{'title':'宿主','desc':'Final Cut Pro / Motion'},{'title':'系统','desc':'macOS 13 或更高版本'},{'title':'架构','desc':'Apple Silicon / Intel'},{'title':'授权','desc':'免费开源,无需激活码'}]},'seo':seo}
|
||||
(ROOT/'releases/1.2.0/article-payload.json').write_text(json.dumps(payload,ensure_ascii=False,indent=2)+'\n')
|
||||
if 'post_id' not in state:
|
||||
existing=request('/wp/v2/posts?slug=dayu-portrait-beauty&context=edit');assert not existing,'Matching post exists; inspect and continue its ID instead of creating a duplicate'
|
||||
boundary='DayuRelease'+uuid.uuid4().hex;parts=[]
|
||||
def part(name,data,filename=None,mime=None):
|
||||
head='--'+boundary+'\r\nContent-Disposition: form-data; name="'+name+'"'
|
||||
if filename:head+='; filename="'+filename+'"'
|
||||
head+='\r\n'
|
||||
if mime:head+='Content-Type: '+mime+'\r\n'
|
||||
parts.append(head.encode()+b'\r\n'+data+b'\r\n')
|
||||
part('payload',json.dumps(payload,ensure_ascii=False).encode())
|
||||
part('package',(ROOT/'releases/1.2.0/大鱼人像美化-1.2.0.pkg').read_bytes(),'Dayu-Portrait-1.2.0.pkg','application/octet-stream')
|
||||
part('cover',(ROOT/'Assets/Artwork/portrait-cover.png').read_bytes(),'Dayu-Portrait-Cover.png','image/png')
|
||||
body=b''.join(parts)+('--'+boundary+'--\r\n').encode()
|
||||
result=request('/dayu-publisher/v1/posts','POST',body,{'Content-Type':'multipart/form-data; boundary='+boundary,'Idempotency-Key':state['operation']})
|
||||
state['response']=result;save()
|
||||
# Response schema may vary; resolve the exact saved slug through standard REST.
|
||||
rows=request('/wp/v2/posts?slug=dayu-portrait-beauty&context=edit');assert len(rows)==1
|
||||
state['post_id']=rows[0]['id'];state['url']=rows[0]['link'];save()
|
||||
saved=request('/dayu-publisher/v1/posts/'+str(state['post_id']))
|
||||
(ROOT/'docs/verification/v1.2.0/published-article.json').write_text(json.dumps(saved,ensure_ascii=False,indent=2)+'\n')
|
||||
print(json.dumps({'post_id':state['post_id'],'url':state['url'],'source':source,'card':card,'snapshot_saved':True},ensure_ascii=False))
|
||||
+2
-1
@@ -15,8 +15,9 @@ xcrun clang "${compiler_extra[@]}" -fobjc-arc -fmodules -Wall -Wextra -Wno-unuse
|
||||
-I "$plugin_dir" -F "$sdk_frameworks" -F "$runtime_frameworks" \
|
||||
-Wl,-rpath,"$runtime_frameworks" \
|
||||
"$plugin_dir/PGBeautyRenderer.m" "$plugin_dir/PGFaceProcessor.m" \
|
||||
"$plugin_dir/PGInspectorState.m" "$plugin_dir/PGInspectorView.m" \
|
||||
"$plugin_dir/beatygrilPlugIn.m" tests/host_adapter_tests.m \
|
||||
-framework Foundation -framework CoreImage -framework CoreGraphics \
|
||||
-framework Foundation -framework AppKit -framework CoreImage -framework CoreGraphics \
|
||||
-framework CoreMedia -framework CoreVideo -framework IOSurface -framework Metal \
|
||||
-framework Vision -framework ImageIO -framework FxPlug -framework PluginManager \
|
||||
-o "$output_dir/host-tests"
|
||||
|
||||
已加载 100/106 个文件。由于本次变更涉及的文件过多,其余文件未显示。
显示更多
在新工单中引用
屏蔽一个用户