比较提交
11
次代码提交
| 作者 | SHA1 | 提交日期 | |
|---|---|---|---|
|
|
d1137edd83 | ||
|
|
2faf7de779 | ||
|
|
e47736222d | ||
|
|
6a29d52a9a | ||
|
|
cd2e608ec6 | ||
|
|
b35b4a5d1c | ||
|
|
83ed140c2e | ||
|
|
7fa3298e02 | ||
|
|
58f09e5756 | ||
|
|
730d1755ca | ||
|
|
2f2c081980 |
没有与搜索条件相匹配的文件
+2
@@ -40,3 +40,5 @@ reference-binaries/
|
|||||||
|
|
||||||
# Reviewed signed release installer (intentional distribution artifact)
|
# Reviewed signed release installer (intentional distribution artifact)
|
||||||
!releases/1.2.0/*.pkg
|
!releases/1.2.0/*.pkg
|
||||||
|
|
||||||
|
!releases/1.3.1/*.pkg
|
||||||
@@ -10,6 +10,12 @@
|
|||||||
|
|
||||||
正式安装路径为 `/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`。美化会随当前帧的人脸自动更新,不需要点击分析。界面以磨皮、美白、瘦脸三张卡片呈现,默认跟随主要人脸;多人模式对多脸变形。单项眼睛可暂时停用并保留数值,右侧箭头恢复默认;磨皮半径、饱和度、锐化和整体混合等收在「更多设置」。旧手动分析与调试参数仅保留隐藏兼容,实际输出统一使用实时自动跟随。
|
||||||
|
|
||||||
|
1.3.0 的磨皮卡片依次提供「磨皮程度、微细锐化、五官保护、非肤色区保护」。两种保护默认 100%;降低五官保护让五官区域参与更多磨皮,降低非肤色区保护则扩大磨皮范围,均不扩大肤色提亮范围。微细锐化在局部支持区域恢复纹理;旧全局磨皮和全局锐化仍保留在更多设置,旧工程的参数与归档继续兼容。迁移证据、实现边界与验证见[新版磨皮参数迁移](docs/analysis/新版磨皮参数迁移.md)。1.3.0 的新增参数已随 1.3.1 一并提供。
|
||||||
|
|
||||||
|
1.3.1(16)修复 V 脸影响眼区的问题,改为四组下颌轮廓到外唇的局部采样,并对齐新目标的 0.065 强度映射。1901 项自动检查通过,APP 与本机 PKG 工坊制作的安装包均已签名、公证和验证。正式 APP 已安装;新效果的原生宿主视觉仍由用户实测。原理与边界见 [V 脸下颌区域修复](docs/analysis/V脸下颌区域修复.md)。
|
||||||
|
|
||||||
|
当前 [1.3.1 正式下载](https://github.com/dayumeida/FCP-BeautyGirl/releases/tag/v1.3.1) 与 [大鱼文章](https://dayu.media/15593/) 已更新,安装包包含「大鱼磨皮瘦脸」分类的配套效果及资源,源码快照也已同步。
|
||||||
|
|
||||||
## 开发和恢复
|
## 开发和恢复
|
||||||
|
|
||||||
需要 Xcode 27.1、FxPlug SDK 4.3.4 与 PluginManager 1.7.4;最低运行系统 macOS 13。正式产物为 arm64/x86_64 通用版本。
|
需要 Xcode 27.1、FxPlug SDK 4.3.4 与 PluginManager 1.7.4;最低运行系统 macOS 13。正式产物为 arm64/x86_64 通用版本。
|
||||||
@@ -23,7 +29,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 副本。
|
工程从 `/Library/Developer/SDKs/FxPlug.sdk` 读取头文件与链接信息,从 `/Library/Developer/Frameworks` 嵌入可运行的 Apple 框架。构建默认使用团队 B3W25FX5H5 的 Developer ID Application 身份;正式交付使用 `python3 scripts/deliver.py` 完成公证、粘附和安装;安装前先关闭 Motion 与 FCP,脚本在检测到宿主仍运行时保留原安装版并停止。原生验收完成后继续脚本的清理阶段,清除临时注册与 APP 副本。
|
||||||
|
|
||||||
公开源码:[GitHub · FCP-BeatyGril](https://github.com/FAMEDaYu/FCP-BeatyGril)。大鱼站备份:`https://dayu.media/git/dayu/paintfcp-fxplug.git`。恢复入口见 [RECOVERY.md](RECOVERY.md),当前验证记录见 [验证记录](docs/验证记录.md)。原始模板保存在 `baseline-20261021` 标签。
|
公开源码:[GitHub · FCP-BeautyGirl](https://github.com/dayumeida/FCP-BeautyGirl)。大鱼站备份:`https://dayu.media/git/dayu/paintfcp-fxplug.git`。恢复入口见 [RECOVERY.md](RECOVERY.md),当前验证记录见 [验证记录](docs/验证记录.md)。原始模板保存在 `baseline-20261021` 标签。
|
||||||
|
|
||||||
继续开发或移交时先读[项目交接文档](docs/项目交接.md),包含当前发布状态、源码入口、坐标与交互约定、验收边界,以及签名打包和双远端保存流程。
|
继续开发或移交时先读[项目交接文档](docs/项目交接.md),包含当前发布状态、源码入口、坐标与交互约定、验收边界,以及签名打包和双远端保存流程。
|
||||||
|
|
||||||
@@ -61,4 +67,4 @@ xcodebuild -project beatygril/beatygril.xcodeproj -scheme 'Wrapper Application'
|
|||||||
|
|
||||||
`large.png`(640×360)和 `small.png`(192×108)与 `.moef` 同目录,安装器安装后重新锁定。手动复制或从 Git 恢复模板时,需对这两个 PNG 重新执行 `chflags uchg`,不能锁定整个模板目录。FCP 当前分类为「大鱼磨皮瘦脸」,Motion 原生滤镜分类仍为「大鱼」。
|
`large.png`(640×360)和 `small.png`(192×108)与 `.moef` 同目录,安装器安装后重新锁定。手动复制或从 Git 恢复模板时,需对这两个 PNG 重新执行 `chflags uchg`,不能锁定整个模板目录。FCP 当前分类为「大鱼磨皮瘦脸」,Motion 原生滤镜分类仍为「大鱼」。
|
||||||
|
|
||||||
GitHub 提供公开源码、提交历史和版本标签;[1.2.0 Release](https://github.com/FAMEDaYu/FCP-BeatyGril/releases/tag/v1.2.0) 提供正式签名公证安装包。大鱼站 Git 备份仍受站点登录与会员网关限制,发布文章另提供公开源码快照;安装包始终免费。
|
GitHub 提供公开源码、提交历史和版本标签;[1.2.0 Release](https://github.com/dayumeida/FCP-BeautyGirl/releases/tag/v1.2.0) 提供正式签名公证安装包。大鱼站 Git 备份仍受站点登录与会员网关限制,发布文章另提供公开源码快照;安装包始终免费。
|
||||||
+27
-2
@@ -1,7 +1,7 @@
|
|||||||
# 恢复说明
|
# 恢复说明
|
||||||
|
|
||||||
- 大鱼备份远程(origin):`https://dayu.media/git/dayu/paintfcp-fxplug.git`
|
- 大鱼备份远程(origin):`https://dayu.media/git/dayu/paintfcp-fxplug.git`
|
||||||
- GitHub 公开远程(github):`https://github.com/FAMEDaYu/FCP-BeatyGril.git`
|
- GitHub 公开远程(github):`https://github.com/dayumeida/FCP-BeautyGirl.git`
|
||||||
- 本地项目根:`/Users/dayu/Documents/PaintFCP`
|
- 本地项目根:`/Users/dayu/Documents/PaintFCP`
|
||||||
- 工作分支:`main`
|
- 工作分支:`main`
|
||||||
- 修改前版本标签:`baseline-20261021`,完整 SHA:`1746f38398588e2367abb37a5c2b31cb64042429`
|
- 修改前版本标签:`baseline-20261021`,完整 SHA:`1746f38398588e2367abb37a5c2b31cb64042429`
|
||||||
@@ -104,4 +104,29 @@ Motion/FCP原生验收范围及未完成项见`docs/verification/v1.1.2/native-q
|
|||||||
|
|
||||||
## GitHub 公开恢复副本
|
## GitHub 公开恢复副本
|
||||||
|
|
||||||
GitHub 远端 `https://github.com/FAMEDaYu/FCP-BeatyGril.git` 已完成完整历史与七个标签的推送,`origin` 及其 main 上游保持大鱼仓库。独立匿名克隆恢复节点为 `356683b41af702d6c0143ad7fa5f540d752d6e14`,197 文件逐字节核验、strict/full fsck 和全部标签回读通过。后续 main 补充发布说明与验证文档,产品源码保持相同。GitHub Release 的公证安装包已匿名下载核对哈希、签名和票据。详见 `docs/GitHub同步说明.md` 和 `docs/verification/github/`。
|
GitHub 远端 `https://github.com/dayumeida/FCP-BeautyGirl.git` 已完成完整历史与七个标签的推送,`origin` 及其 main 上游保持大鱼仓库。独立匿名克隆恢复节点为 `356683b41af702d6c0143ad7fa5f540d752d6e14`,197 文件逐字节核验、strict/full fsck 和全部标签回读通过。后续 main 补充发布说明与验证文档,产品源码保持相同。GitHub Release 的公证安装包已匿名下载核对哈希、签名和票据。详见 `docs/GitHub同步说明.md` 和 `docs/verification/github/`。
|
||||||
|
|
||||||
|
|
||||||
|
## 1.3.0 新面板参数恢复
|
||||||
|
|
||||||
|
签名源码节点 `2f2c081980dc610ac810f8daf92da8966f310802` 已推送并读回核验 origin 与 github 的 `codex/new-beauty-controls` 分支。两个远端地址沿用本文既有配置。独立远端克隆与 strict/full fsck、205 个受跟踪文件逐字节比较通过,见 `docs/verification/v1.3.0/recovery-verification.json`。无 LFS、子模块;依赖与签名凭据仍需单独恢复。
|
||||||
|
|
||||||
|
本节点增加五官保护强度、非肤色区保护和局部微细锐化,PGBeautySettings v2 兼容旧 160 字节 v1 状态,面板归档仍为 schema 1。测试命令不变,1636 项通过。恢复签名源码时克隆并切换该分支,或 `git switch --detach 2f2c081980dc610ac810f8daf92da8966f310802`。
|
||||||
|
|
||||||
|
1.3.0(15)已正式安装到 `/Library/Plug-Ins/FxPlug/beatygril.app`。签名、通用架构、公证票据、Gatekeeper 和 PluginKit 正式路径均通过,已清理本次构建、旧 APP 备份和公证 ZIP。最终回执见 `docs/verification/v1.3.0/delivery-receipt.json`、`installed-verification.json`;`prepared-release.json` 保留安装前状态。`v1.3.0` 标签包含签名源码及后续交付文档。本次没有生成新 PKG 或更新文章,1.2.0 的公开发行资料继续有效;新滑杆的原生宿主视觉仍由用户测试。
|
||||||
|
|
||||||
|
|
||||||
|
## 1.3.1 V 脸下颌修复恢复
|
||||||
|
|
||||||
|
签名源码完整 SHA 为 `7fa3298e022946e77dfd8cfba1bb5ff2c2a6361f`,APP 1.3.1(16),分支 `codex/v-face-jaw-fix`。origin 与 github 同名分支已推送并回读一致。已从 origin 独立克隆该节点,219个受跟踪文件字节和执行位一致、strict/full fsck及Xcode工程读取通过,无LFS或子模块;见 `docs/verification/v1.3.1/recovery-verification.json`。恢复依赖和签名入口沿用本文现有配置,用户帧和私钥不作为源码依赖保存。
|
||||||
|
|
||||||
|
V 脸现在为四组下颌到外唇局部 pin,映射为 V×0.065,无新版目标已取消的旧尺寸增益。此前瘦脸/下巴/短脸/下颌骨仍保留旧版本映射,大眼/窄脸/颧骨/小头和整体组合保留近似边界。1901项全套及1561项重叠真实帧几何ASan通过。Apple公证 Accepted `e353b701-2fc2-4fcc-b03e-69af7735400f`、staple/validate通过;已在用户确认退出宿主后完成1.3.1正式安装、身份/票据/注册核验和临时副本清理。后续FCP仍有后台进程,通过正常退出菜单结束后才替换APP。PKG由本机PKG工坊CLI制作,包含用户指定分类4项资源,Installer签名、公证Accepted 78105340-306f-4629-8670-772760d4cb9a、票据、Gatekeeper、载荷及隔离脚本均通过;详见同版本交付回执和验证记录。
|
||||||
|
|
||||||
|
|
||||||
|
1.3.1最终发行标签为 `v1.3.1`,完整SHA `b35b4a5d1ca252d2f83c798613560a768274de3c`。GitHub已匿名独立克隆恢复并比对232个受跟踪文件的字节/执行位、strict/full fsck和恢复PKG签名/票据;产品源码与签名节点7fa3298完全一致。见 `docs/verification/v1.3.1/final-tag-recovery.json`。两个远端main纳入该标签后继续记录交付文档,不移动标签。文章15593和GitHub/大鱼Release已更新,GitHub与文章安装包/源码下载字节均校验,完整入口见 `docs/GitHub同步说明.md`。
|
||||||
|
|
||||||
|
|
||||||
|
GitHub于2026-10-22更正仓库名为 `FCP-BeautyGirl`,远端为 `https://github.com/dayumeida/FCP-BeautyGirl.git`。更名后的恢复检查点 `6a29d52a9ad667665da4acf75017c008fa0a566d` 已通过新地址匿名独立克隆、239个文件字节/执行位和strict/full fsck核验,产品源码与此前正式版相同。Release、附件和原有标签保留,文章及本地远程/仓库目录表均已更新;详见 `docs/verification/github/rename-recovery-20261022.json`。
|
||||||
|
|
||||||
|
|
||||||
|
GitHub用户名现为 `dayumeida`,账号ID117798680保持一致。当前公开远端 `https://github.com/dayumeida/FCP-BeautyGirl.git` 的恢复检查点 `2faf7de77929cf7707645ebb38095b03d5729647` 已通过匿名独立克隆、241文件字节/执行位和strict/full fsck核验,产品源码与账号更名前一致。两个仓库的全部分支/标签及Release附件均保留;当前文章、本地github远端和仓库目录表已同步。回执见 `docs/verification/github/username-recovery-20261022.json`。
|
||||||
@@ -17,9 +17,9 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>XPC!</string>
|
<string>XPC!</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.2.0</string>
|
<string>1.3.1</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>14</string>
|
<string>16</string>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
<string></string>
|
<string></string>
|
||||||
<key>PlugInKit</key>
|
<key>PlugInKit</key>
|
||||||
@@ -72,7 +72,7 @@
|
|||||||
<key>uuid</key>
|
<key>uuid</key>
|
||||||
<string>FB14B0E6-59B2-4836-916F-CD0C559E9241</string>
|
<string>FB14B0E6-59B2-4836-916F-CD0C559E9241</string>
|
||||||
<key>version</key>
|
<key>version</key>
|
||||||
<string>1.2.0</string>
|
<string>1.3.1</string>
|
||||||
</dict>
|
</dict>
|
||||||
<dict>
|
<dict>
|
||||||
<key>className</key>
|
<key>className</key>
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
<key>uuid</key>
|
<key>uuid</key>
|
||||||
<string>E0E432C8-3D71-4ABA-B006-E7F519E06C87</string>
|
<string>E0E432C8-3D71-4ABA-B006-E7F519E06C87</string>
|
||||||
<key>version</key>
|
<key>version</key>
|
||||||
<string>1.2.0</string>
|
<string>1.3.1</string>
|
||||||
<key>protocolNames</key>
|
<key>protocolNames</key>
|
||||||
<array>
|
<array>
|
||||||
<string>FxOnScreenControl</string>
|
<string>FxOnScreenControl</string>
|
||||||
|
|||||||
@@ -82,6 +82,26 @@ static NSDictionary<NSString *, CIColorKernel *> *PGKernels(void) {
|
|||||||
"kernel vec4 pgMaskedMix(__sample source,__sample effect,__sample mask,float amount) {"
|
"kernel vec4 pgMaskedMix(__sample source,__sample effect,__sample mask,float amount) {"
|
||||||
"vec4 s=unpremultiply(source); vec4 e=unpremultiply(effect);"
|
"vec4 s=unpremultiply(source); vec4 e=unpremultiply(effect);"
|
||||||
"return premultiply(vec4(mix(s.rgb,e.rgb,clamp(mask.r*amount,0.0,1.0)),s.a)); }");
|
"return premultiply(vec4(mix(s.rgb,e.rgb,clamp(mask.r*amount,0.0,1.0)),s.a)); }");
|
||||||
|
// Keep smoothing support separate from the exposure/color mask. The
|
||||||
|
// two new controls independently release feature cutouts and background.
|
||||||
|
k[@"smoothingSupport"] = PGKernel(@
|
||||||
|
"kernel vec4 pgSmoothingSupport(__sample protectedMask,__sample baseMask,float featureProtection,float skinProtection) {"
|
||||||
|
"float base=clamp(baseMask.r,0.0,1.0);float protectedValue=clamp(protectedMask.r,0.0,1.0);"
|
||||||
|
"float value=mix(base,protectedValue,featureProtection);"
|
||||||
|
"if(skinProtection<1.0){float extended=1.0-skinProtection*(1.0-base);"
|
||||||
|
"float retention=base>0.00001?clamp(protectedValue/base,0.0,1.0):1.0;"
|
||||||
|
"value=extended*mix(1.0,retention,featureProtection);}"
|
||||||
|
"return vec4(value,value,value,1.0); }");
|
||||||
|
k[@"microDetail"] = PGKernel(@
|
||||||
|
"kernel vec4 pgMicroDetail(__sample processed,__sample original,__sample low,__sample coverage,float amount) {"
|
||||||
|
"vec4 outPixel=unpremultiply(processed);vec3 src=unpremultiply(original).rgb;"
|
||||||
|
"vec3 high=src-unpremultiply(low).rgb;vec3 weights=vec3(0.299,0.587,0.114);"
|
||||||
|
"float y=dot(src,weights);float detailY=dot(high,weights);float size=length(high);"
|
||||||
|
"float lightGate=smoothstep(0.07,0.22,y)*(1.0-smoothstep(0.86,0.99,y));"
|
||||||
|
"float textureGate=smoothstep(0.0015,0.010,size)*(1.0-smoothstep(0.16,0.36,size));"
|
||||||
|
"float gain=amount*clamp(coverage.r,0.0,1.0)*lightGate*textureGate*1.18;"
|
||||||
|
"vec3 detail=vec3(detailY*0.92)+(high-vec3(detailY))*0.28;"
|
||||||
|
"return premultiply(vec4(clamp(outPixel.rgb+detail*gain,0.0,1.0),outPixel.a)); }");
|
||||||
k[@"colorCoverage"] = PGKernel(@
|
k[@"colorCoverage"] = PGKernel(@
|
||||||
"kernel vec4 pgColorCoverage(__sample coverage,__sample image,float invert) {"
|
"kernel vec4 pgColorCoverage(__sample coverage,__sample image,float invert) {"
|
||||||
"vec3 c=unpremultiply(image).rgb; float y=dot(c,vec3(0.299,0.587,0.114));"
|
"vec3 c=unpremultiply(image).rgb; float y=dot(c,vec3(0.299,0.587,0.114));"
|
||||||
@@ -157,7 +177,7 @@ static NSDictionary<NSString *, CIColorKernel *> *PGKernels(void) {
|
|||||||
float mix = PGClamp(s.mix, 0, 1);
|
float mix = PGClamp(s.mix, 0, 1);
|
||||||
if (mix == 0 && !s.showMask) return image;
|
if (mix == 0 && !s.showMask) return image;
|
||||||
NSDictionary *k = PGKernels();
|
NSDictionary *k = PGKernels();
|
||||||
if (k.count != 11) {
|
if (k.count != 13) {
|
||||||
if (error) *error = [NSError errorWithDomain:@"PGBeautyRenderer" code:2
|
if (error) *error = [NSError errorWithDomain:@"PGBeautyRenderer" code:2
|
||||||
userInfo:@{NSLocalizedDescriptionKey:@"The beauty image kernels could not be compiled."}];
|
userInfo:@{NSLocalizedDescriptionKey:@"The beauty image kernels could not be compiled."}];
|
||||||
return nil;
|
return nil;
|
||||||
@@ -173,7 +193,10 @@ static NSDictionary<NSString *, CIColorKernel *> *PGKernels(void) {
|
|||||||
float sharpness = PGClamp(s.sharpness,0,2);
|
float sharpness = PGClamp(s.sharpness,0,2);
|
||||||
float teeth = PGClamp(s.teethWhitening,0,1);
|
float teeth = PGClamp(s.teethWhitening,0,1);
|
||||||
float eyes = PGClamp(s.eyeBrightening,0,1);
|
float eyes = PGClamp(s.eyeBrightening,0,1);
|
||||||
BOOL needsSkin = local*amount>0 || brightness>0 || hue!=0 || saturation!=0 || even>0 ||
|
float protection = s.featureProtection ? PGClamp(s.featureProtectionAmount,0,1) : 0;
|
||||||
|
float skinProtection = PGClamp(s.nonSkinProtection,0,1);
|
||||||
|
float micro = PGClamp(s.microDetail,0,1)*amount;
|
||||||
|
BOOL needsSkin = local*amount>0 || micro>0 || brightness>0 || hue!=0 || saturation!=0 || even>0 ||
|
||||||
(s.skinTonePreset>0 && s.skinTonePreset<=6) || (s.showMask && s.debugChannel==0);
|
(s.skinTonePreset>0 && s.skinTonePreset<=6) || (s.showMask && s.debugChannel==0);
|
||||||
CIImage *black = [[CIImage imageWithColor:[CIColor colorWithRed:0 green:0 blue:0 alpha:1]] imageByCroppingToRect:extent];
|
CIImage *black = [[CIImage imageWithColor:[CIColor colorWithRed:0 green:0 blue:0 alpha:1]] imageByCroppingToRect:extent];
|
||||||
CIImage *teethInput = teethMask ? [teethMask imageByCroppingToRect:extent] : black;
|
CIImage *teethInput = teethMask ? [teethMask imageByCroppingToRect:extent] : black;
|
||||||
@@ -200,12 +223,29 @@ static NSDictionary<NSString *, CIColorKernel *> *PGKernels(void) {
|
|||||||
float invert = s.debugChannel==1 || s.debugChannel==2 ? (s.invertMask ? 1 : 0) : 0;
|
float invert = s.debugChannel==1 || s.debugChannel==2 ? (s.invertMask ? 1 : 0) : 0;
|
||||||
return PGApply(k[@"debug"],extent,@[image,preview,@(invert)]);
|
return PGApply(k[@"debug"],extent,@[image,preview,@(invert)]);
|
||||||
}
|
}
|
||||||
|
CIImage *smoothingMask=mask;
|
||||||
|
if((local*amount>0 || micro>0) && (protection<1 || skinProtection<1)) {
|
||||||
|
CIImage *baseRaw=colorMask ? [colorMask imageByCroppingToRect:extent] : raw;
|
||||||
|
float soft=PGClamp(s.maskSoftness,0,1);
|
||||||
|
CIImage *baseMask=PGApply(k[@"mask"],extent,@[baseRaw,PGBlur(baseRaw,0.35+soft*radius*0.35,extent),
|
||||||
|
image,PGBlur(image,1.5,extent),eyeInput,teethInput,
|
||||||
|
@(PGClamp(s.maskThreshold,0,255)/255.f),@(soft),@(PGClamp(s.maskEdgeProtect,0,1)),@0,@(s.invertMask?1:0)]);
|
||||||
|
smoothingMask=PGApply(k[@"smoothingSupport"],extent,@[mask,baseMask,@(protection),@(skinProtection)]);
|
||||||
|
}
|
||||||
// Detail protection may contain small eye, brow, nose and lip cutouts.
|
// Detail protection may contain small eye, brow, nose and lip cutouts.
|
||||||
// Exposure and color must not reuse those holes or sharpen a face ellipse
|
// Exposure and color must not reuse those holes or sharpen a face ellipse
|
||||||
// with the detail threshold; that creates visible rings and islands.
|
// with the detail threshold; that creates visible rings and islands.
|
||||||
CIImage *toneMask=mask;
|
CIImage *toneMask=mask;
|
||||||
BOOL changesColor=brightness>0 || hue!=0 || saturation!=0 || even>0 ||
|
BOOL changesColor=brightness>0 || hue!=0 || saturation!=0 || even>0 ||
|
||||||
(s.skinTonePreset>0 && s.skinTonePreset<=6);
|
(s.skinTonePreset>0 && s.skinTonePreset<=6);
|
||||||
|
if(!colorMask && changesColor && s.featureProtection) {
|
||||||
|
// Standalone callers may not supply the separate uncut coverage.
|
||||||
|
// At least keep our eye/teeth cutouts out of their color adjustment.
|
||||||
|
float soft=PGClamp(s.maskSoftness,0,1);
|
||||||
|
toneMask=PGApply(k[@"mask"],extent,@[raw,PGBlur(raw,0.35+soft*radius*0.35,extent),
|
||||||
|
image,PGBlur(image,1.5,extent),eyeInput,teethInput,
|
||||||
|
@(PGClamp(s.maskThreshold,0,255)/255.f),@(soft),@(PGClamp(s.maskEdgeProtect,0,1)),@0,@(s.invertMask?1:0)]);
|
||||||
|
}
|
||||||
if(colorMask && changesColor) {
|
if(colorMask && changesColor) {
|
||||||
CGFloat feather=MAX(3,MIN(extent.size.width,extent.size.height)*0.03);
|
CGFloat feather=MAX(3,MIN(extent.size.width,extent.size.height)*0.03);
|
||||||
CIImage *support=[colorMask imageByCroppingToRect:extent];
|
CIImage *support=[colorMask imageByCroppingToRect:extent];
|
||||||
@@ -219,8 +259,12 @@ static NSDictionary<NSString *, CIColorKernel *> *PGKernels(void) {
|
|||||||
CGFloat broad = fmin(16,(2.55+0.45*local)*fine+1.1+1.3*local);
|
CGFloat broad = fmin(16,(2.55+0.45*local)*fine+1.1+1.3*local);
|
||||||
float compression = 0.12+2.4*powf(local,2.15)+1.25*local;
|
float compression = 0.12+2.4*powf(local,2.15)+1.25*local;
|
||||||
CIImage *detail = PGApply(k[@"detail"],extent,@[result,PGBlur(result,fine,extent),
|
CIImage *detail = PGApply(k[@"detail"],extent,@[result,PGBlur(result,fine,extent),
|
||||||
PGBlur(result,broad,extent),@(compression),@(s.featureProtection ? 1 : 0)]);
|
PGBlur(result,broad,extent),@(compression),@(protection)]);
|
||||||
result = PGApply(k[@"maskedMix"],extent,@[result,detail,mask,@(amount)]);
|
result = PGApply(k[@"maskedMix"],extent,@[result,detail,smoothingMask,@(amount)]);
|
||||||
|
}
|
||||||
|
if(micro>0) {
|
||||||
|
CGFloat fine=fmin(7.4,2.275+3.15*pow(local,0.72)+0.95*local);
|
||||||
|
result=PGApply(k[@"microDetail"],extent,@[result,image,PGBlur(image,fine,extent),smoothingMask,@(micro)]);
|
||||||
}
|
}
|
||||||
CIImage *color = result;
|
CIImage *color = result;
|
||||||
if (brightness>0) {
|
if (brightness>0) {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
// Persisted FxPlug render state. Append fields and bump version for migrations.
|
// Persisted FxPlug render state. Append fields and bump version for migrations.
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -12,11 +14,17 @@ typedef struct {
|
|||||||
float faceJumpGuardRange, selectionX, selectionY;
|
float faceJumpGuardRange, selectionX, selectionY;
|
||||||
uint32_t featureProtection, skinTonePreset, showMask, invertMask, debugChannel;
|
uint32_t featureProtection, skinTonePreset, showMask, invertMask, debugChannel;
|
||||||
uint32_t showFaceBoxes, showTrackBox, singleFace, fastMultiFace, trackingEnabled, targetIndex;
|
uint32_t showFaceBoxes, showTrackBox, singleFace, fastMultiFace, trackingEnabled, targetIndex;
|
||||||
|
// v2 keeps the entire 160-byte v1 prefix, including legacy bool/strength IDs.
|
||||||
|
float featureProtectionAmount, nonSkinProtection, microDetail;
|
||||||
} PGBeautySettings;
|
} PGBeautySettings;
|
||||||
|
|
||||||
|
enum { PGBeautySettingsV1Size = 160, PGBeautySettingsVersion = 2 };
|
||||||
|
_Static_assert(offsetof(PGBeautySettings,featureProtectionAmount)==PGBeautySettingsV1Size,
|
||||||
|
"The v1 render-state prefix must remain stable");
|
||||||
|
|
||||||
static inline PGBeautySettings PGBeautyDefaults(void) {
|
static inline PGBeautySettings PGBeautyDefaults(void) {
|
||||||
PGBeautySettings s = {0};
|
PGBeautySettings s = {0};
|
||||||
s.version = 1;
|
s.version = PGBeautySettingsVersion;
|
||||||
s.amount = 1.0f;
|
s.amount = 1.0f;
|
||||||
s.radius = 8.0f;
|
s.radius = 8.0f;
|
||||||
s.mix = 1.0f;
|
s.mix = 1.0f;
|
||||||
@@ -24,6 +32,8 @@ static inline PGBeautySettings PGBeautyDefaults(void) {
|
|||||||
s.maskSoftness = 0.6f;
|
s.maskSoftness = 0.6f;
|
||||||
s.maskEdgeProtect = 0.4f;
|
s.maskEdgeProtect = 0.4f;
|
||||||
s.featureProtection = 1;
|
s.featureProtection = 1;
|
||||||
|
s.featureProtectionAmount = 1;
|
||||||
|
s.nonSkinProtection = 1;
|
||||||
s.faceJumpGuardRange = 1.8f;
|
s.faceJumpGuardRange = 1.8f;
|
||||||
s.selectionX = s.selectionY = 0.5f;
|
s.selectionX = s.selectionY = 0.5f;
|
||||||
return s;
|
return s;
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ static const uint8_t PGThinPins[17][2] = {
|
|||||||
{69,49},{65,49},{68,52},{66,52},{67,47}
|
{69,49},{65,49},{68,52},{66,52},{67,47}
|
||||||
};
|
};
|
||||||
static const uint8_t PGChinPins[3][2] = {{68,38},{67,37},{66,36}};
|
static const uint8_t PGChinPins[3][2] = {{68,38},{67,37},{66,36}};
|
||||||
|
// Lower contour to lip anchors, in the 2.4.0 V-face shader's update order.
|
||||||
|
static const uint8_t PGVFacePins[4][2] = {{69,39},{65,35},{70,26},{64,34}};
|
||||||
// Origin and radius neighbour; all six directions point toward landmark 29.
|
// 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}};
|
static const uint8_t PGJawPins[6][2] = {{72,71},{62,63},{71,70},{63,64},{70,71},{64,63}};
|
||||||
|
|
||||||
@@ -347,11 +349,11 @@ static NSDictionary *PGStableGeometry(NSDictionary *current, NSDictionary *previ
|
|||||||
_lock = [[NSLock alloc] init];
|
_lock = [[NSLock alloc] init];
|
||||||
_realtime = [PGRealtimeFaceTracker new];
|
_realtime = [PGRealtimeFaceTracker new];
|
||||||
_targets = [NSMutableArray arrayWithObjects:[NSMutableDictionary dictionary], [NSMutableDictionary dictionary], [NSMutableDictionary dictionary], nil];
|
_targets = [NSMutableArray arrayWithObjects:[NSMutableDictionary dictionary], [NSMutableDictionary dictionary], [NSMutableDictionary dictionary], nil];
|
||||||
// Thin/chin/jaw/short reconstruct the recovered local coordinate math.
|
// Thin/V/chin/jaw/short reconstruct recovered local coordinate math.
|
||||||
// The other five controls retain this project's analytic approximation.
|
// Four other controls retain this project's analytic approximation.
|
||||||
// Recovered jaw -> chin -> short order follows the reference. The other
|
// Recovered V -> jaw -> chin -> short order follows the reference. Other
|
||||||
// analytic body controls still precede jaw, so the full combined chain
|
// analytic body controls still precede V, so the full combined chain
|
||||||
// (narrow/V/cheek/small-head/eyes) is not claimed reference-equivalent.
|
// with narrow/cheek/small-head/eyes is not claimed reference-equivalent.
|
||||||
// Each pin changes the sampling coordinate, with only one image resample.
|
// Each pin changes the sampling coordinate, with only one image resample.
|
||||||
NSMutableString *source = [NSMutableString stringWithString:
|
NSMutableString *source = [NSMutableString stringWithString:
|
||||||
@"vec2 pgThinPin(vec2 q, vec4 control, float ratio) {"
|
@"vec2 pgThinPin(vec2 q, vec4 control, float ratio) {"
|
||||||
@@ -371,8 +373,9 @@ static NSDictionary *PGStableGeometry(NSDictionary *current, NSDictionary *previ
|
|||||||
" float dy=b.y-a.y; if(abs(dy)<=0.000001) dy=dy<0.0?-0.000001:0.000001;"
|
" 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; }"
|
" if(q.x<a.x+(b.x-a.x)*(q.y-a.y)/dy) crossing=1.0; }"
|
||||||
" return vec2(distance,crossing); }"
|
" 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"];
|
" kernel vec2 pgFaceWarp(vec2 c, vec2 size, vec2 le, vec2 re, vec2 er, float thinRatio, float bigEye, float shortRatio, float narrow, float chinRatio, float vRatio, 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<17; i++) [source appendFormat:@", vec4 pin%lu",(unsigned long)i];
|
||||||
|
for (NSUInteger i=0; i<4; i++) [source appendFormat:@", vec4 vPin%lu",(unsigned long)i];
|
||||||
for (NSUInteger i=0; i<3; i++) [source appendFormat:@", vec4 chinPin%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"];
|
[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<6; i++) [source appendFormat:@", vec4 jawPin%lu",(unsigned long)i];
|
||||||
@@ -383,14 +386,14 @@ static NSDictionary *PGStableGeometry(NSDictionary *current, NSDictionary *previ
|
|||||||
[source appendString:@" q.y=1.0-q.y; p=imageOrigin+q*imageSize; }"
|
[source appendString:@" q.y=1.0-q.y; p=imageOrigin+q*imageSize; }"
|
||||||
" vec2 n = (p-c)/size;"
|
" vec2 n = (p-c)/size;"
|
||||||
" float body = 1.0-smoothstep(0.38,0.76,length(n*vec2(1.0,0.82)));"
|
" float body = 1.0-smoothstep(0.38,0.76,length(n*vec2(1.0,0.82)));"
|
||||||
" float lower = (1.0-smoothstep(-0.12,0.27,n.y))*body;"
|
|
||||||
" float sides = smoothstep(0.08,0.32,abs(n.x));"
|
" float sides = smoothstep(0.08,0.32,abs(n.x));"
|
||||||
" float j=(n.y+0.23)/0.22; float jawBand = exp(-j*j)*body;"
|
|
||||||
" float ch=(n.y-0.02)/0.19; float cheekBand = exp(-ch*ch)*body;"
|
" 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)));"
|
" 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 += (p-c)*smallHead*0.18*head;"
|
||||||
" p.x += size.x*n.x*(narrow*0.30*body + vFace*0.24*jawBand + cheek*0.18*cheekBand)*sides;"
|
" p.x += size.x*n.x*(narrow*0.30*body + cheek*0.18*cheekBand)*sides;"
|
||||||
" p.y += size.y*(-vFace*0.025*lower);"
|
" if(vRatio>0.000001) { vec2 q=(p-imageOrigin)/imageSize; q.y=1.0-q.y;"];
|
||||||
|
for (NSUInteger i=0; i<4; i++) [source appendFormat:@" q=pgThinPin(q,vPin%lu,vRatio);",(unsigned long)i];
|
||||||
|
[source appendString:@" q.y=1.0-q.y; p=imageOrigin+q*imageSize; }"
|
||||||
" if(abs(jawRatio)>0.000001) { vec2 q=(p-imageOrigin)/imageSize; q.y=1.0-q.y;"];
|
" 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];
|
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; }"
|
[source appendString:@" q.y=1.0-q.y; p=imageOrigin+q*imageSize; }"
|
||||||
@@ -527,7 +530,7 @@ static NSDictionary *PGStableGeometry(NSDictionary *current, NSDictionary *previ
|
|||||||
for (NSDictionary *face in geometry) {
|
for (NSDictionary *face in geometry) {
|
||||||
CGRect box = PGArrayRect(face[@"box"]), pixels = PGToPixels(box, extent);
|
CGRect box = PGArrayRect(face[@"box"]), pixels = PGToPixels(box, extent);
|
||||||
NSArray *points76 = PGSanitizedPoints76(face[@"points76"]);
|
NSArray *points76 = PGSanitizedPoints76(face[@"points76"]);
|
||||||
float thinRatio = 0, chinRatio = 0, jawRatio = 0, shortRatio = 0;
|
float thinRatio = 0, chinRatio = 0, jawRatio = 0, shortRatio = 0, vRatio = 0;
|
||||||
if (points76) {
|
if (points76) {
|
||||||
double width = hypot(([points76[75][0] doubleValue]-[points76[59][0] doubleValue])*extent.size.width,
|
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);
|
([points76[75][1] doubleValue]-[points76[59][1] doubleValue])*extent.size.height);
|
||||||
@@ -541,18 +544,21 @@ static NSDictionary *PGStableGeometry(NSDictionary *current, NSDictionary *previ
|
|||||||
chinRatio = chin*(chin>0 ? .04 : .02)*gain;
|
chinRatio = chin*(chin>0 ? .04 : .02)*gain;
|
||||||
jawRatio = PGClamp(s.faceJaw,0,1)*.05*gain;
|
jawRatio = PGClamp(s.faceJaw,0,1)*.05*gain;
|
||||||
shortRatio = PGClamp(s.faceShort,-1,1)*.25*gain;
|
shortRatio = PGClamp(s.faceShort,-1,1)*.25*gain;
|
||||||
|
// The specified 2.4.0 shader takes the CPU-scaled .065 V ratio
|
||||||
|
// directly; it no longer applies the older shader's size gain.
|
||||||
|
vRatio = PGClamp(s.faceV,0,1)*.065;
|
||||||
}
|
}
|
||||||
BOOL otherControls = s.faceBigEye || s.faceNarrow || s.faceV || s.faceCheekbone || s.faceSmallHead;
|
BOOL otherControls = s.faceBigEye || s.faceNarrow || s.faceCheekbone || s.faceSmallHead;
|
||||||
// No full topology means no guessed bbox thin/chin/jaw/short deformation. Legacy
|
// No full topology means no guessed bbox thin/V/chin/jaw/short deformation. Legacy
|
||||||
// archives using only those controls remain exact image/mask pass-through.
|
// archives using only those controls remain exact image/mask pass-through.
|
||||||
if (thinRatio <= .000001 && fabsf(chinRatio) <= .000001 && fabsf(jawRatio) <= .000001 && fabsf(shortRatio) <= .000001 && !otherControls) continue;
|
if (thinRatio <= .000001 && vRatio <= .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 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));
|
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));
|
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],
|
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)],
|
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)), @(shortRatio),
|
@(thinRatio), @(PGClamp(s.faceBigEye, 0, 1)), @(shortRatio),
|
||||||
@(PGClamp(s.faceNarrow, -1, 1)), @(chinRatio), @(PGClamp(s.faceV, 0, 1)),
|
@(PGClamp(s.faceNarrow, -1, 1)), @(chinRatio), @(vRatio),
|
||||||
@(jawRatio), @(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];
|
[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++) {
|
for (NSUInteger i=0; i<17; i++) {
|
||||||
@@ -564,6 +570,15 @@ static NSDictionary *PGStableGeometry(NSDictionary *current, NSDictionary *previ
|
|||||||
}
|
}
|
||||||
[arguments addObject:[CIVector vectorWithX:ox Y:oy Z:ax W:ay]];
|
[arguments addObject:[CIVector vectorWithX:ox Y:oy Z:ax W:ay]];
|
||||||
}
|
}
|
||||||
|
for (NSUInteger i=0; i<4; i++) {
|
||||||
|
double ox=0,oy=0,ax=0,ay=0;
|
||||||
|
if (points76) {
|
||||||
|
NSArray *origin=points76[PGVFacePins[i][0]],*anchor=points76[PGVFacePins[i][1]];
|
||||||
|
ox=[origin[0] doubleValue];oy=1-[origin[1] doubleValue];
|
||||||
|
ax=[anchor[0] doubleValue];ay=1-[anchor[1] doubleValue];
|
||||||
|
}
|
||||||
|
[arguments addObject:[CIVector vectorWithX:ox Y:oy Z:ax W:ay]];
|
||||||
|
}
|
||||||
for (NSUInteger i=0; i<3; i++) {
|
for (NSUInteger i=0; i<3; i++) {
|
||||||
double ox=0,oy=0,ax=0,ay=0;
|
double ox=0,oy=0,ax=0,ay=0;
|
||||||
if (points76) {
|
if (points76) {
|
||||||
|
|||||||
@@ -11,9 +11,10 @@ NSArray<NSDictionary *> *PGInspectorControls(void) {
|
|||||||
#define ITEM(ID,NAME,FIELD,SECTION,KIND,FORMAT,MIN,MAX,BYPASS,STORAGE) \
|
#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}]
|
[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(PGSmoothingEnabled,"磨皮",featureProtection,"smooth","master","integer",0,1,NO,"master");
|
||||||
ITEM(PGMidtone,"局部磨皮",midtoneDetail,"smooth","slider","percent",0,1,YES,"float");
|
ITEM(PGMidtone,"磨皮程度",midtoneDetail,"smooth","slider","percent",0,1,YES,"float");
|
||||||
ITEM(PGHighPass,"全局磨皮",highPassDetail,"smooth","slider","percent",0,1,YES,"float");
|
ITEM(PGMicroDetail,"微细锐化",microDetail,"smooth","slider","percent",0,1,YES,"float");
|
||||||
ITEM(PGFeatureProtect,"五官保护",featureProtection,"smooth","toggle","integer",0,1,NO,"uint");
|
ITEM(PGFeatureProtectionAmount,"五官保护",featureProtectionAmount,"smooth","slider","percent",0,1,YES,"float");
|
||||||
|
ITEM(PGNonSkinProtection,"非肤色区保护",nonSkinProtection,"smooth","slider","percent",0,1,YES,"float");
|
||||||
ITEM(PGColorEnabled,"美白",featureProtection,"color","master","integer",0,1,NO,"master");
|
ITEM(PGColorEnabled,"美白",featureProtection,"color","master","integer",0,1,NO,"master");
|
||||||
ITEM(PGTonePreset,"肤色",skinTonePreset,"color","preset","integer",0,6,NO,"uint");
|
ITEM(PGTonePreset,"肤色",skinTonePreset,"color","preset","integer",0,6,NO,"uint");
|
||||||
NSMutableDictionary *preset=[items.lastObject mutableCopy];
|
NSMutableDictionary *preset=[items.lastObject mutableCopy];
|
||||||
@@ -22,9 +23,10 @@ NSArray<NSDictionary *> *PGInspectorControls(void) {
|
|||||||
ITEM(PGTeeth,"白牙",teethWhitening,"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(PGHue,"皮肤色相",skinHue,"color","slider","degrees",-30,30,YES,"float");
|
||||||
ITEM(PGShapeEnabled,"瘦脸",featureProtection,"shape","master","integer",0,1,NO,"master");
|
ITEM(PGShapeEnabled,"瘦脸",featureProtection,"shape","master","integer",0,1,NO,"master");
|
||||||
ITEM(PGTrackBoxes,"人脸框预览",showTrackBox,"shape","toggle","integer",0,1,NO,"uint");
|
ITEM(PGTrackBoxes,"检测框预览",showTrackBox,"shape","toggle","integer",0,1,NO,"uint");
|
||||||
ITEM(PGFastMulti,"多人模式",fastMultiFace,"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");
|
NSMutableDictionary *multi=[items.lastObject mutableCopy];
|
||||||
|
multi[@"tooltip"]=@"多人模式易抖动,请谨慎使用";items[items.count-1]=multi;
|
||||||
ITEM(PGThin,"瘦脸",faceThin,"shape","slider","signed",0,1,YES,"float");
|
ITEM(PGThin,"瘦脸",faceThin,"shape","slider","signed",0,1,YES,"float");
|
||||||
ITEM(PGBigEye,"大眼",faceBigEye,"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(PGShort,"短脸",faceShort,"shape","slider","signed",-1,1,YES,"float");
|
||||||
@@ -36,11 +38,13 @@ NSArray<NSDictionary *> *PGInspectorControls(void) {
|
|||||||
ITEM(PGSmallHead,"小头",faceSmallHead,"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(PGSmallHeadRange,"小头范围",faceSmallHeadRange,"shape","slider","percent",0,1,NO,"float");
|
||||||
ITEM(PGAmount,"磨皮强度",amount,"more","slider","percent",0,1,YES,"float");
|
ITEM(PGAmount,"磨皮强度",amount,"more","slider","percent",0,1,YES,"float");
|
||||||
|
ITEM(PGHighPass,"全局磨皮",highPassDetail,"more","slider","percent",0,1,YES,"float");
|
||||||
ITEM(PGRadius,"磨皮半径",radius,"more","slider","radius",1,30,NO,"float");
|
ITEM(PGRadius,"磨皮半径",radius,"more","slider","radius",1,30,NO,"float");
|
||||||
ITEM(PGSaturation,"肤色饱和度",skinSaturation,"more","slider","percent",-100,100,YES,"float");
|
ITEM(PGSaturation,"肤色饱和度",skinSaturation,"more","slider","percent",-100,100,YES,"float");
|
||||||
ITEM(PGEvenTone,"均匀肤色",skinEvenTone,"more","slider","percent",0,1,YES,"float");
|
ITEM(PGEvenTone,"均匀肤色",skinEvenTone,"more","slider","percent",0,1,YES,"float");
|
||||||
ITEM(PGEyeBright,"眼睛提亮",eyeBrightening,"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(PGSharpness,"锐化",sharpness,"more","slider","percent",0,1,YES,"float");
|
||||||
|
ITEM(PGJumpGuard,"防跳脸范围",faceJumpGuardRange,"more","slider","percent",1,4,NO,"float");
|
||||||
ITEM(PGMix,"整体混合",mix,"more","slider","percent",0,1,NO,"float");
|
ITEM(PGMix,"整体混合",mix,"more","slider","percent",0,1,NO,"float");
|
||||||
#undef ITEM
|
#undef ITEM
|
||||||
controls=[items copy];
|
controls=[items copy];
|
||||||
@@ -62,7 +66,12 @@ static double PGControlValue(id raw,NSDictionary *d) {
|
|||||||
+ (instancetype)stateWithValues:(NSDictionary *)values disabledParameters:(NSSet *)disabled hasEdits:(BOOL)edited {
|
+ (instancetype)stateWithValues:(NSDictionary *)values disabledParameters:(NSSet *)disabled hasEdits:(BOOL)edited {
|
||||||
PGInspectorState *state=[self new]; NSMutableDictionary *safe=[NSMutableDictionary new];NSMutableSet *bypassed=[NSMutableSet new];
|
PGInspectorState *state=[self new]; NSMutableDictionary *safe=[NSMutableDictionary new];NSMutableSet *bypassed=[NSMutableSet new];
|
||||||
for(NSDictionary *d in PGInspectorControls()) {
|
for(NSDictionary *d in PGInspectorControls()) {
|
||||||
NSNumber *key=d[@"id"];safe[key]=@(PGControlValue(values[key],d));
|
NSNumber *key=d[@"id"];id raw=values[key];
|
||||||
|
// Schema 1 documents stored five-feature protection as the legacy bool.
|
||||||
|
// New numeric values win when present; old archives gain its exact 0/1.
|
||||||
|
if(key.unsignedIntValue==PGFeatureProtectionAmount && !raw && [values[@(PGFeatureProtect)] isKindOfClass:NSNumber.class])
|
||||||
|
raw=@([values[@(PGFeatureProtect)] boolValue]?1:0);
|
||||||
|
safe[key]=@(PGControlValue(raw,d));
|
||||||
if([d[@"bypass"] boolValue] && [disabled containsObject:key]) [bypassed addObject:key];
|
if([d[@"bypass"] boolValue] && [disabled containsObject:key]) [bypassed addObject:key];
|
||||||
}
|
}
|
||||||
state->_values=[safe copy];state->_disabledParameters=[bypassed copy];state->_hasEdits=edited;return state;
|
state->_values=[safe copy];state->_disabledParameters=[bypassed copy];state->_hasEdits=edited;return state;
|
||||||
@@ -115,7 +124,8 @@ PGBeautySettings PGSettingsWithInspectorState(PGBeautySettings s,PGInspectorStat
|
|||||||
if([storage isEqual:@"float"]) *(float *)((uint8_t *)&s+offset)=value;
|
if([storage isEqual:@"float"]) *(float *)((uint8_t *)&s+offset)=value;
|
||||||
else *(uint32_t *)((uint8_t *)&s+offset)=(uint32_t)value;
|
else *(uint32_t *)((uint8_t *)&s+offset)=(uint32_t)value;
|
||||||
}
|
}
|
||||||
if(![state valueForParameter:PGSmoothingEnabled])s.amount=s.midtoneDetail=s.highPassDetail=0;
|
s.featureProtection=s.featureProtectionAmount>0;
|
||||||
|
if(![state valueForParameter:PGSmoothingEnabled])s.amount=s.midtoneDetail=s.highPassDetail=s.microDetail=0;
|
||||||
if(![state valueForParameter:PGColorEnabled]) {s.skinBrightness=s.skinHue=s.skinSaturation=s.skinEvenTone=s.teethWhitening=s.eyeBrightening=0;s.skinTonePreset=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;}
|
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;
|
return s;
|
||||||
@@ -189,6 +199,12 @@ PGBeautySettings PGSettingsWithInspectorState(PGBeautySettings s,PGInspectorStat
|
|||||||
else [api getFloatValue:&value fromParameter:key atTime:time];
|
else [api getFloatValue:&value fromParameter:key atTime:time];
|
||||||
values[@(key)]=@(value);
|
values[@(key)]=@(value);
|
||||||
}
|
}
|
||||||
|
// The old bool can still disable protection. A true value must preserve a
|
||||||
|
// numeric setting already read above; its registered default is one for
|
||||||
|
// old documents that have no independently stored numeric parameter.
|
||||||
|
BOOL legacyFeatureProtection=YES;
|
||||||
|
if([api getBoolValue:&legacyFeatureProtection fromParameter:PGFeatureProtect atTime:time] && !legacyFeatureProtection)
|
||||||
|
values[@(PGFeatureProtectionAmount)]=@0;
|
||||||
BOOL legacyDetail=YES;[api getBoolValue:&legacyDetail fromParameter:PGDetailEnabled atTime:time];
|
BOOL legacyDetail=YES;[api getBoolValue:&legacyDetail fromParameter:PGDetailEnabled atTime:time];
|
||||||
NSMutableSet *disabled=legacyDetail?[NSMutableSet set]:[NSMutableSet setWithObjects:@(PGHighPass),@(PGSharpness),nil];
|
NSMutableSet *disabled=legacyDetail?[NSMutableSet set]:[NSMutableSet setWithObjects:@(PGHighPass),@(PGSharpness),nil];
|
||||||
// Earlier versions put high-pass smoothing under a different group. Keep
|
// Earlier versions put high-pass smoothing under a different group. Keep
|
||||||
@@ -196,7 +212,7 @@ PGBeautySettings PGSettingsWithInspectorState(PGBeautySettings s,PGInspectorStat
|
|||||||
// off, rather than turning a still-active global effect off on first edit.
|
// off, rather than turning a still-active global effect off on first edit.
|
||||||
if(![values[@(PGSmoothingEnabled)] boolValue] && legacyDetail && [values[@(PGHighPass)] doubleValue]>0) {
|
if(![values[@(PGSmoothingEnabled)] boolValue] && legacyDetail && [values[@(PGHighPass)] doubleValue]>0) {
|
||||||
values[@(PGSmoothingEnabled)]=@YES;
|
values[@(PGSmoothingEnabled)]=@YES;
|
||||||
[disabled addObject:@(PGMidtone)];[disabled addObject:@(PGAmount)];
|
[disabled addObject:@(PGMidtone)];[disabled addObject:@(PGAmount)];[disabled addObject:@(PGMicroDetail)];
|
||||||
}
|
}
|
||||||
_state=[PGInspectorState stateWithValues:values disabledParameters:disabled hasEdits:NO];
|
_state=[PGInspectorState stateWithValues:values disabledParameters:disabled hasEdits:NO];
|
||||||
}
|
}
|
||||||
@@ -232,6 +248,11 @@ PGBeautySettings PGSettingsWithInspectorState(PGBeautySettings s,PGInspectorStat
|
|||||||
else if([d[@"kind"] isEqual:@"toggle"] || [d[@"kind"] isEqual:@"master"]) [api setBoolValue:value!=0 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];
|
else [api setFloatValue:value toParameter:key atTime:time];
|
||||||
}
|
}
|
||||||
|
BOOL featureProtection=[next isParameterEnabled:PGFeatureProtectionAmount] && [next valueForParameter:PGFeatureProtectionAmount]>0;
|
||||||
|
id<FxParameterRetrievalAPI_v6> reader=[_manager apiForProtocol:@protocol(FxParameterRetrievalAPI_v6)];
|
||||||
|
BOOL previousProtection=!featureProtection;
|
||||||
|
if(![reader getBoolValue:&previousProtection fromParameter:PGFeatureProtect atTime:time] || previousProtection!=featureProtection)
|
||||||
|
[api setBoolValue:featureProtection toParameter:PGFeatureProtect atTime:time];
|
||||||
_state=next;_pendingUntil=NSDate.timeIntervalSinceReferenceDate+.35;_pendingTime=time;_statusMessage=@"";return YES;
|
_state=next;_pendingUntil=NSDate.timeIntervalSinceReferenceDate+.35;_pendingTime=time;_statusMessage=@"";return YES;
|
||||||
}
|
}
|
||||||
- (BOOL)setValue:(double)value forParameter:(UInt32)identifier {
|
- (BOOL)setValue:(double)value forParameter:(UInt32)identifier {
|
||||||
|
|||||||
@@ -202,6 +202,8 @@ static PGInspectorGlyphButton *PGUIButton(NSString *glyph, NSColor *accent, id t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
NSString *key=[descriptor[@"id"]stringValue];
|
NSString *key=[descriptor[@"id"]stringValue];
|
||||||
|
NSString *tooltip=descriptor[@"tooltip"];
|
||||||
|
if(tooltip.length){self.toolTip=tooltip;_label.toolTip=tooltip;_toggle.toolTip=tooltip;_slider.toolTip=tooltip;_valueField.toolTip=tooltip;}
|
||||||
_slider.identifier=[@"pg.value."stringByAppendingString:key];_valueField.identifier=[@"pg.number."stringByAppendingString:key];
|
_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];
|
_toggle.identifier=[@"pg.toggle."stringByAppendingString:key];_eye.identifier=[@"pg.enable."stringByAppendingString:key];_reset.identifier=[@"pg.reset."stringByAppendingString:key];
|
||||||
}return self;
|
}return self;
|
||||||
@@ -282,11 +284,11 @@ static PGInspectorGlyphButton *PGUIButton(NSString *glyph, NSColor *accent, id t
|
|||||||
@implementation PGInspectorCard
|
@implementation PGInspectorCard
|
||||||
- (BOOL)isFlipped { return YES; }
|
- (BOOL)isFlipped { return YES; }
|
||||||
- (CGFloat)heightForWidth:(CGFloat)width {
|
- (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;
|
if(!_expanded)return 44;CGFloat height=49;for(PGInspectorRow *row in _rows){height+=[row heightForWidth:MAX(0,width-24)];if([row.descriptor[@"id"]unsignedIntValue]==PGFastMulti)height+=8;}return height+7;
|
||||||
}
|
}
|
||||||
- (void)layout {
|
- (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);
|
[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;}
|
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]==PGFastMulti)y+=8;}
|
||||||
}
|
}
|
||||||
- (void)drawRect:(NSRect)dirtyRect {
|
- (void)drawRect:(NSRect)dirtyRect {
|
||||||
(void)dirtyRect;NSBezierPath *shape=[NSBezierPath bezierPathWithRoundedRect:NSInsetRect(self.bounds,.5,.5) xRadius:13 yRadius:13];
|
(void)dirtyRect;NSBezierPath *shape=[NSBezierPath bezierPathWithRoundedRect:NSInsetRect(self.bounds,.5,.5) xRadius:13 yRadius:13];
|
||||||
@@ -294,7 +296,7 @@ static PGInspectorGlyphButton *PGUIButton(NSString *glyph, NSColor *accent, id t
|
|||||||
[[_accent colorWithAlphaComponent:.22]setStroke];shape.lineWidth=.8;[shape stroke];
|
[[_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];
|
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];
|
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];}
|
for(PGInspectorRow *row in _rows)if([row.descriptor[@"id"]unsignedIntValue]==PGFastMulti){CGFloat y=NSMaxY(row.frame)+4;[line removeAllPoints];[line moveToPoint:NSMakePoint(12,y)];[line lineToPoint:NSMakePoint(NSWidth(self.bounds)-12,y)];[line stroke];}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -14,5 +14,6 @@ enum {
|
|||||||
PGTrackData1=60, PGTrackData2=61, PGTrackData3=62,
|
PGTrackData1=60, PGTrackData2=61, PGTrackData3=62,
|
||||||
PGSmoothingEnabled=70, PGColorEnabled=71, PGShapeEnabled=72,
|
PGSmoothingEnabled=70, PGColorEnabled=71, PGShapeEnabled=72,
|
||||||
PGDetailEnabled=73, PGHelp=74,
|
PGDetailEnabled=73, PGHelp=74,
|
||||||
|
PGFeatureProtectionAmount=75, PGNonSkinProtection=76, PGMicroDetail=77,
|
||||||
PGInspectorPanel=210
|
PGInspectorPanel=210
|
||||||
};
|
};
|
||||||
@@ -30,6 +30,9 @@ static const PGSlider PGSliders[] = {
|
|||||||
PGS(PGEyeBright,"眼睛提亮",eyeBrightening,0,1,.01,YES),
|
PGS(PGEyeBright,"眼睛提亮",eyeBrightening,0,1,.01,YES),
|
||||||
PGS(PGHighPass,"高频细节平滑",highPassDetail,0,1,.01,YES),
|
PGS(PGHighPass,"高频细节平滑",highPassDetail,0,1,.01,YES),
|
||||||
PGS(PGSharpness,"锐化",sharpness,0,1,.01,YES),
|
PGS(PGSharpness,"锐化",sharpness,0,1,.01,YES),
|
||||||
|
PGS(PGFeatureProtectionAmount,"五官保护强度",featureProtectionAmount,0,1,.01,YES),
|
||||||
|
PGS(PGNonSkinProtection,"非肤色区保护",nonSkinProtection,0,1,.01,YES),
|
||||||
|
PGS(PGMicroDetail,"微细锐化",microDetail,0,1,.01,YES),
|
||||||
PGS(PGThin,"瘦脸",faceThin,0,1,.01,YES),
|
PGS(PGThin,"瘦脸",faceThin,0,1,.01,YES),
|
||||||
PGS(PGBigEye,"大眼",faceBigEye,0,1,.01,YES),
|
PGS(PGBigEye,"大眼",faceBigEye,0,1,.01,YES),
|
||||||
PGS(PGShort,"短脸",faceShort,-1,1,.01,NO),
|
PGS(PGShort,"短脸",faceShort,-1,1,.01,NO),
|
||||||
@@ -141,6 +144,7 @@ static CIImage *PGImageFromTile(FxImageTile *tile) {
|
|||||||
void (^end)(void)=^{ok=[api endParameterSubGroup]&&ok;};
|
void (^end)(void)=^{ok=[api endParameterSubGroup]&&ok;};
|
||||||
group(@"磨皮",100); toggle(@"启用磨皮",PGSmoothingEnabled,YES);
|
group(@"磨皮",100); toggle(@"启用磨皮",PGSmoothingEnabled,YES);
|
||||||
slider(PGAmount); slider(PGMidtone); slider(PGRadius); toggle(@"保护五官",PGFeatureProtect,YES); end();
|
slider(PGAmount); slider(PGMidtone); slider(PGRadius); toggle(@"保护五官",PGFeatureProtect,YES); end();
|
||||||
|
group(@"磨皮保护与细节",106); slider(PGFeatureProtectionAmount); slider(PGNonSkinProtection); slider(PGMicroDetail); end();
|
||||||
group(@"肤色",101); toggle(@"启用肤色",PGColorEnabled,YES);
|
group(@"肤色",101); toggle(@"启用肤色",PGColorEnabled,YES);
|
||||||
ok=[api addPopupMenuWithName:@"肤色风格" parameterID:PGTonePreset defaultValue:0 menuEntries:@[@"原色",@"自然白",@"冷白",@"暖白",@"红润白",@"小麦",@"古铜"] parameterFlags:kFxParameterFlag_HIDDEN]&&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();
|
slider(PGBrightness); slider(PGHue); slider(PGSaturation); slider(PGEvenTone); slider(PGTeeth); slider(PGEyeBright); end();
|
||||||
@@ -207,7 +211,7 @@ static CIImage *PGImageFromTile(FxImageTile *tile) {
|
|||||||
double x=.5,y=.5; [api getXValue:&x YValue:&y fromParameter:PGSelection atTime:time];
|
double x=.5,y=.5; [api getXValue:&x YValue:&y fromParameter:PGSelection atTime:time];
|
||||||
s.selectionX=isfinite(x)?fmin(1,fmax(0,x)):.5; s.selectionY=isfinite(y)?fmin(1,fmax(0,y)):.5;
|
s.selectionX=isfinite(x)?fmin(1,fmax(0,x)):.5; s.selectionY=isfinite(y)?fmin(1,fmax(0,y)):.5;
|
||||||
BOOL enabled=YES;
|
BOOL enabled=YES;
|
||||||
[api getBoolValue:&enabled fromParameter:PGSmoothingEnabled atTime:time]; if(!enabled) s.amount=s.midtoneDetail=0;
|
[api getBoolValue:&enabled fromParameter:PGSmoothingEnabled atTime:time]; if(!enabled) s.amount=s.midtoneDetail=s.microDetail=0;
|
||||||
enabled=YES; [api getBoolValue:&enabled fromParameter:PGColorEnabled atTime:time];
|
enabled=YES; [api getBoolValue:&enabled fromParameter:PGColorEnabled atTime:time];
|
||||||
if(!enabled) {s.skinBrightness=s.skinHue=s.skinSaturation=s.skinEvenTone=s.teethWhitening=s.eyeBrightening=0;s.skinTonePreset=0;}
|
if(!enabled) {s.skinBrightness=s.skinHue=s.skinSaturation=s.skinEvenTone=s.teethWhitening=s.eyeBrightening=0;s.skinTonePreset=0;}
|
||||||
enabled=YES; [api getBoolValue:&enabled fromParameter:PGDetailEnabled atTime:time]; if(!enabled) s.highPassDetail=s.sharpness=0;
|
enabled=YES; [api getBoolValue:&enabled fromParameter:PGDetailEnabled atTime:time]; if(!enabled) s.highPassDetail=s.sharpness=0;
|
||||||
@@ -263,9 +267,16 @@ static CIImage *PGImageFromTile(FxImageTile *tile) {
|
|||||||
- (BOOL)renderDestinationImage:(FxImageTile *)destination sourceImages:(NSArray<FxImageTile *> *)sources pluginState:(NSData *)state atTime:(CMTime)time error:(NSError **)error {
|
- (BOOL)renderDestinationImage:(FxImageTile *)destination sourceImages:(NSArray<FxImageTile *> *)sources pluginState:(NSData *)state atTime:(CMTime)time error:(NSError **)error {
|
||||||
if(sources.count==0||!sources.firstObject.ioSurface||!destination.ioSurface||![state isKindOfClass:NSData.class]||state.length==0||state.length>320*1024*1024) return PGFail(error,@"宿主传入的画面或状态无效。");
|
if(sources.count==0||!sources.firstObject.ioSurface||!destination.ioSurface||![state isKindOfClass:NSData.class]||state.length==0||state.length>320*1024*1024) return PGFail(error,@"宿主传入的画面或状态无效。");
|
||||||
NSDictionary *plist=[NSPropertyListSerialization propertyListWithData:state options:NSPropertyListImmutable format:nil error:error];
|
NSDictionary *plist=[NSPropertyListSerialization propertyListWithData:state options:NSPropertyListImmutable format:nil error:error];
|
||||||
if(![plist isKindOfClass:NSDictionary.class]||![plist[@"settings"] isKindOfClass:NSData.class]||[plist[@"settings"] length]!=sizeof(PGBeautySettings)) return PGFail(error,@"渲染状态版本无效。");
|
if(![plist isKindOfClass:NSDictionary.class]||![plist[@"settings"] isKindOfClass:NSData.class]) return PGFail(error,@"渲染状态版本无效。");
|
||||||
PGBeautySettings s;[plist[@"settings"] getBytes:&s length:sizeof(s)];
|
NSData *encoded=plist[@"settings"]; uint32_t version=0;
|
||||||
if(s.version!=1) return PGFail(error,@"此版本不支持保存的渲染状态。");
|
if(encoded.length<sizeof(version)) return PGFail(error,@"渲染状态版本无效。");
|
||||||
|
[encoded getBytes:&version length:sizeof(version)];
|
||||||
|
PGBeautySettings s=PGBeautyDefaults();
|
||||||
|
if(version==1 && encoded.length==PGBeautySettingsV1Size) {
|
||||||
|
[encoded getBytes:&s length:PGBeautySettingsV1Size];s.version=PGBeautySettingsVersion;
|
||||||
|
} else if(version==PGBeautySettingsVersion && encoded.length==sizeof(s)) {
|
||||||
|
[encoded getBytes:&s length:sizeof(s)];
|
||||||
|
} else return PGFail(error,@"此版本不支持保存的渲染状态。");
|
||||||
s.trackingEnabled=0;s.singleFace=0;
|
s.trackingEnabled=0;s.singleFace=0;
|
||||||
CIImage *input=PGImageFromTile(sources.firstObject); if(!input) return PGFail(error,@"无法读取 IOSurface。");
|
CIImage *input=PGImageFromTile(sources.firstObject); if(!input) return PGFail(error,@"无法读取 IOSurface。");
|
||||||
@synchronized(self) {
|
@synchronized(self) {
|
||||||
|
|||||||
@@ -17,9 +17,9 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.2.0</string>
|
<string>1.3.1</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>14</string>
|
<string>16</string>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
|
|||||||
+24
-5
@@ -1,10 +1,10 @@
|
|||||||
# GitHub 同步与恢复
|
# GitHub 同步与恢复
|
||||||
|
|
||||||
公开源码:https://github.com/FAMEDaYu/FCP-BeatyGril
|
公开源码:https://github.com/dayumeida/FCP-BeautyGirl
|
||||||
|
|
||||||
正式安装包:https://github.com/FAMEDaYu/FCP-BeatyGril/releases/tag/v1.2.0
|
正式安装包:https://github.com/dayumeida/FCP-BeautyGirl/releases/tag/v1.3.1
|
||||||
|
|
||||||
本地 `github` 远端指向上述 GitHub 仓库,`origin` 继续指向大鱼仓库,`main` 仍跟随 `origin/main`。当前项目的完整提交历史、main 和七个版本标签已推送,两端引用与本地一致;未改写历史或覆盖标签。后续更新需要分别推送到 origin 和 github,不会自动定时同步。
|
本地 `github` 远端指向上述 GitHub 仓库,`origin` 继续指向大鱼仓库,`main` 仍跟随 `origin/main`。当前项目的完整提交历史、main 和全部现有版本标签已推送,两端引用与本地一致;未改写历史或覆盖标签。后续更新需要分别推送到 origin 和 github,不会自动定时同步。
|
||||||
|
|
||||||
## 已验证的 GitHub 恢复节点
|
## 已验证的 GitHub 恢复节点
|
||||||
|
|
||||||
@@ -13,11 +13,30 @@
|
|||||||
安装包:`Dayu-Portrait-1.2.0.pkg`,5,011,005 bytes,SHA-256 `5bd77c28b3a7e54f3d4060be1f16bcb145d9bc4d6b9cfb9eb137c01130440243`。Release 标签 `v1.2.0` 仍指向 `3c7261c3a861b9f4704cfb36055d7b500c239dab`,没有重新编译或改动正式安装包。
|
安装包:`Dayu-Portrait-1.2.0.pkg`,5,011,005 bytes,SHA-256 `5bd77c28b3a7e54f3d4060be1f16bcb145d9bc4d6b9cfb9eb137c01130440243`。Release 标签 `v1.2.0` 仍指向 `3c7261c3a861b9f4704cfb36055d7b500c239dab`,没有重新编译或改动正式安装包。
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone https://github.com/FAMEDaYu/FCP-BeatyGril.git
|
git clone https://github.com/dayumeida/FCP-BeautyGirl.git
|
||||||
cd FCP-BeatyGril
|
cd FCP-BeautyGirl
|
||||||
git switch --detach 356683b41af702d6c0143ad7fa5f540d752d6e14
|
git switch --detach 356683b41af702d6c0143ad7fa5f540d752d6e14
|
||||||
```
|
```
|
||||||
|
|
||||||
本项目没有 LFS 或子模块。编译仍需自行安装 Xcode 和 Apple FxPlug SDK;签名私钥与公证凭据不在 Git 内。GitHub 公共克隆无需登录;大鱼备份仍受站点登录和会员网关限制。两边都只承诺已经提交、推送并核验的节点。
|
本项目没有 LFS 或子模块。编译仍需自行安装 Xcode 和 Apple FxPlug SDK;签名私钥与公证凭据不在 Git 内。GitHub 公共克隆无需登录;大鱼备份仍受站点登录和会员网关限制。两边都只承诺已经提交、推送并核验的节点。
|
||||||
|
|
||||||
源代码编写署名:代码由 OpenAI + Codex 全程编写,站长仅参与调试和测试。
|
源代码编写署名:代码由 OpenAI + Codex 全程编写,站长仅参与调试和测试。
|
||||||
|
|
||||||
|
|
||||||
|
## 1.3.1 当前发行与恢复
|
||||||
|
|
||||||
|
main已快进纳入1.3.0磨皮参数和1.3.1的V脸下颌修复;origin和github的main及v1.3.1引用已回读核验。v1.3.1固定完整SHA为 `b35b4a5d1ca252d2f83c798613560a768274de3c`,签名APP源码为 `7fa3298e022946e77dfd8cfba1bb5ff2c2a6361f`。独立匿名GitHub克隆232文件字节与执行位一致、strict/full fsck和恢复PKG签名/票据通过。无LFS或子模块。后续main仅补充发布记录,不改写标签。
|
||||||
|
|
||||||
|
GitHub Release已提供 `Dayu-Portrait-1.3.1.pkg`(5,011,286 bytes,SHA256 `a6bb7262c9066e7abca8d1f21eaeaf1ba6a1d0d51b4859d39307aa7d7b0bc927`)和源码ZIP(9,682,532 bytes,SHA256 `7c335f54717a73e710ef484fc9f7b90864211c128a0f217bf170aa092333f228`)。两者匿名重新下载哈希一致,PKG签名和票据通过。源码快照基于上述v1.3.1节点,排除PKG本体而保留全部构建源码、模板、素材与验证文档。
|
||||||
|
|
||||||
|
大鱼站同名Release附件上传与名称/大小回读通过,下载入口仍受站点登录保护;未宣称API令牌可直接回读该浏览器附件入口。文章15593已就地更新,主包和源码链接的新文件哈希通过,免费规则、封面、作者、分类及未修改资源字段保持一致。正常URL和新请求的前台版本、安装/使用/故障说明及三项SEO均核验,未登录页面显示免费及登录获取入口;未下测试订单。详见verification/v1.3.1。
|
||||||
|
|
||||||
|
|
||||||
|
## 2026-10-22 仓库名称拼写更正
|
||||||
|
|
||||||
|
GitHub仓库从 `FAMEDaYu/FCP-BeatyGril` 更名为 `FAMEDaYu/FCP-BeautyGirl`,仓库ID `1428817365` 保持一致。更名前后全部分支/标签SHA、Release与附件ID/大小一致,未另建仓库或改写历史。本地github远端已改为 `https://github.com/dayumeida/FCP-BeautyGirl.git`,origin和main上游仍为原大鱼仓库。现有发布标签、已公证APP/PKG及源码快照保留原始字节,旧快照里的旧链接由GitHub重定向到新地址;当前文档、文章和Release说明使用新地址。插件APP产品名、Bundle ID和安装路径沿用原配置。更名核验见verification/github/rename-20261022.json。
|
||||||
|
|
||||||
|
|
||||||
|
## 2026-10-22 用户名更改
|
||||||
|
|
||||||
|
用户将GitHub登录名从 `FAMEDaYu` 改为 `dayumeida`,账号ID117798680保持不变。两个公开仓库迁移到dayumeida名下:FCP-BeautyGirl(ID1428817365)和FCPX-(ID904215594)。更名前后全部分支/标签SHA及Release/附件ID、名称、大小一致。当前项目github远端为 `https://github.com/dayumeida/FCP-BeautyGirl.git`,origin及main上游保持原大鱼仓库;文章15593、活动文档和Release说明更新为新地址。正式版本标签、公证APP/PKG和源码快照保留原始字节,不为账号更名重新构建。个人主页新地址为 `https://github.com/dayumeida`,旧个人主页不提供重定向;仓库旧地址由GitHub重定向。历史验证回执保留当时用户名。账号更名核验见verification/github/username-20261022.json。
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# V 脸下颌区域修复
|
||||||
|
|
||||||
|
2026-10-22,针对用户报告「V 脸作用到眼睛,应作用于下颌」。目标为本机 MSE18V2 2.4.0(20260724150000),产品修复版本为 1.3.1(16)。
|
||||||
|
|
||||||
|
## 原因与修改
|
||||||
|
|
||||||
|
1.3.0 仍使用脸框上的宽范围高斯带及纵向位移近似 V 脸。它不依赖真实下颌轮廓,在真实人像上能测到眼区变化。不是面板标签或参数 ID 接错;本次只替换 V 脸几何场。
|
||||||
|
|
||||||
|
新版目标的 AIR 按 `69→39、65→35、70→26、64→34` 顺序执行四组局部逆向采样。起点都是下颌轮廓,锚点都是外唇。每步使用前一步更新后的 q:
|
||||||
|
|
||||||
|
```text
|
||||||
|
r = length(anchor − origin)
|
||||||
|
if r > 0.0001:
|
||||||
|
weight = clamp(1 − length(q − origin) / r, 0, 1)
|
||||||
|
q = clamp(q − (anchor − origin) × ratio × weight, 0, 1)
|
||||||
|
```
|
||||||
|
|
||||||
|
CPU 函数 `0x10002fb74` 从参数 `+0x90` 读取 V 值,乘位于 `0x100085ea0` 的 double `0.065` 和公共强度,再转 float 写入 shape 结构的 `+8`。新版三个 shader helper 都没有旧版的 `280/尺寸` 增益;本项目 V 使用 `clamp(faceV,0,1)×0.065`,整体混合沿用现有产品链。公共强度各调用方的完整来源尚未追踪,不将整体混合宣称为原版该参数的等价重建。
|
||||||
|
|
||||||
|
生产实现复用局部 pin 核心,四步严格顺序更新,与图像/四张支持蒙版共享同一坐标场;完整 76 点缺失或不合法时安全直通。Core Image 坐标和关键点一起转换到 Y 向下的全图 UV,结束后一起转回,支持非零图像原点、横竖画幅和歪头。
|
||||||
|
|
||||||
|
## 静态证据与复现
|
||||||
|
|
||||||
|
原始 APP、metallib、完整 AIR 与用户帧仅留本机 build,不是构建依赖。源码没有嵌入目标二进制、私有模型或授权逻辑。
|
||||||
|
|
||||||
|
- 原 APP 嵌套 Mach-O(fat)SHA-256:`577303e0de72e07feb576c882e82382b3f056b023a7e987d990ffb37f6b16109`。
|
||||||
|
- arm64 切片 SHA-256:`afc71d0d21fd3c1ca95ca745ddafcd729e36ad95f17d8638009f2e45d9b7e8df`。
|
||||||
|
- metallib SHA-256:`856849c45cea2c8f083eeba06a9e318c450f4a56694b438d1a5ba8ef752a0ee0`。
|
||||||
|
- `xcrun metal-objdump --metallib --disassemble` 输出 SHA-256:`1d5bd8d5a3917afbb3b4fe1c40b44480760614102685ac826e84ad2e30a450f9`;V 分支 3364–3431 行,四组点索引在 3374、3376、3378、3380 行。
|
||||||
|
- 精简 AIR 摘录:[vface-air.txt](vface-air.txt);CPU 字段/系数与分析条件:[vface-findings.json](vface-findings.json);REA CPU 汇编和常量证据:[rea-vface-evidence.json](rea-vface-evidence.json)。REA 操作为只读,未运行原 APP 或原 shader。
|
||||||
|
|
||||||
|
## 验证
|
||||||
|
|
||||||
|
修改前隔离 V 测试复现:NASA 人像瞳孔 6 的位移约 0.031493 像素;宿主物理行序测试四种原点组合的 V 输出与独立 upright 参考均不符。原宿主输出两眼区像素变化量分别为 12824、19304,下颌为 195206。
|
||||||
|
|
||||||
|
修改后瞳孔检查通过;宿主四种原点组合的 V 眼区像素变化量均为 0,下颌为 39246,独立 upright 对照最大差 1/255、均差 0.000124/255。数值场覆盖 4 种画幅、正侧脸和歪头、半强度、小脸不带旧增益、顺序组合、缺点/坏点以及四张蒙版同步。独立的真实解剖位置检查要求下颌有位移且两瞳孔位移小于 0.002 像素,避免只用复制实现的数值公式验收。
|
||||||
|
|
||||||
|
完整自动回归:1901 项通过(57 Beauty、116 Face、1033 几何、370 Host ASan、98 OSC ASan、227 Inspector ASan)。真实用户帧专项另记交付验证;与基础用例重叠,不相加。正式安装和原生宿主 V 脸视觉验收状态见 [验证记录](../验证记录.md)。
|
||||||
|
|
||||||
|
## 范围
|
||||||
|
|
||||||
|
V 脸现在加入已恢复的五项局部控制。瘦脸、下巴、短脸、下颌骨沿用此前版本的尺寸增益;未将新版目标的其他差异混入本次修复。大眼、窄脸、颧骨和小头仍为近似,整条混合控制链的数学、顺序和最终像素尚未全部对齐。静态重建、离屏/接口检查不等于原版 shader 的运行时逐像素比较,也不等于 Motion/FCP 原生视觉验收。
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"artifacts":[{"architecture":"arm64","digest":{"sha256":"afc71d0d21fd3c1ca95ca745ddafcd729e36ad95f17d8638009f2e45d9b7e8df"},"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_32b6ee46d4fb5eab563883ba73e35a458d5da21229b8977fddcb4df656517152","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>.","Bytes reflect Ghidra's initialized memory, including loader fixups. Reads stop at an unmapped or uninitialized byte and report completeness explicitly.","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":{"address":"0x100085ea0","bytes_hex":"a4703d0ad7a3b03f9a9999999999a93f","complete":true,"requested_bytes":16,"returned_bytes":16},"operation":"read_bytes","parameters":{"address":"0x100085ea0","length":16},"predicate_type":"rea.analysis","provider":{"id":"ghidra","name":"Ghidra","version":"12.1.4"},"raw_result":{"address":"0x100085ea0","bytes_hex":"a4703d0ad7a3b03f9a9999999999a93f","complete":true,"requested_bytes":16,"returned_bytes":16},"subject":{"architecture":"arm64","digest":{"sha256":"afc71d0d21fd3c1ca95ca745ddafcd729e36ad95f17d8638009f2e45d9b7e8df"},"format":"mach-o","local_path":"/Users/dayu/Documents/PaintFCP/build/body-analysis/MSE18V2-arm64","name":"MSE18V2-arm64"}},{"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_6a88310573f4dfd2f23feef7d12c3f58eba366db1717861770656d55375e07a1","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 dataLine truncated
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
MSE18V2 2.4.0 AIR excerpt; original complete-text line numbers.
|
||||||
|
SHA256: 1d5bd8d5a3917afbb3b4fe1c40b44480760614102685ac826e84ad2e30a450f9
|
||||||
|
|
||||||
|
3364: 158: ; preds = %109, %85
|
||||||
|
3365: %159 = phi <2 x float> [ %155, %109 ], [ %86, %85 ]
|
||||||
|
3366: %160 = tail call fast float @air.fast_fabs.f32(float %5) #2
|
||||||
|
3367: %161 = fcmp fast ogt float %160, 0x3EB0C6F7A0000000
|
||||||
|
3368: br i1 %161, label %162, label %209
|
||||||
|
3369:
|
||||||
|
3370: 162: ; preds = %158
|
||||||
|
3371: %163 = bitcast [4 x <2 x i32>]* %17 to i8*
|
||||||
|
3372: call void @llvm.lifetime.start.p0i8(i64 32, i8* nonnull %163) #10
|
||||||
|
3373: %164 = getelementptr inbounds [4 x <2 x i32>], [4 x <2 x i32>]* %17, i64 0, i64 0
|
||||||
|
3374: store <2 x i32> <i32 69, i32 39>, <2 x i32>* %164, align 8
|
||||||
|
3375: %165 = getelementptr inbounds [4 x <2 x i32>], [4 x <2 x i32>]* %17, i64 0, i64 1
|
||||||
|
3376: store <2 x i32> <i32 65, i32 35>, <2 x i32>* %165, align 8
|
||||||
|
3377: %166 = getelementptr inbounds [4 x <2 x i32>], [4 x <2 x i32>]* %17, i64 0, i64 2
|
||||||
|
3378: store <2 x i32> <i32 70, i32 26>, <2 x i32>* %166, align 8
|
||||||
|
3379: %167 = getelementptr inbounds [4 x <2 x i32>], [4 x <2 x i32>]* %17, i64 0, i64 3
|
||||||
|
3380: store <2 x i32> <i32 64, i32 34>, <2 x i32>* %167, align 8
|
||||||
|
3381: %168 = insertelement <2 x float> undef, float %5, i64 0
|
||||||
|
3382: %169 = shufflevector <2 x float> %168, <2 x float> undef, <2 x i32> zeroinitializer
|
||||||
|
3383: br label %171
|
||||||
|
3384:
|
||||||
|
3385: 170: ; preds = %205
|
||||||
|
3386: call void @llvm.lifetime.end.p0i8(i64 32, i8* nonnull %163) #10
|
||||||
|
3387: br label %209
|
||||||
|
3388:
|
||||||
|
3389: 171: ; preds = %205, %162
|
||||||
|
3390: %172 = phi <2 x float> [ %159, %162 ], [ %206, %205 ]
|
||||||
|
3391: %173 = phi i32 [ 0, %162 ], [ %207, %205 ]
|
||||||
|
3392: %174 = zext i32 %173 to i64
|
||||||
|
3393: %175 = getelementptr inbounds [4 x <2 x i32>], [4 x <2 x i32>]* %17, i64 0, i64 %174
|
||||||
|
3394: %176 = load <2 x i32>, <2 x i32>* %175, align 8
|
||||||
|
3395: %177 = extractelement <2 x i32> %176, i64 0
|
||||||
|
3396: %178 = add nsw i32 %177, %2
|
||||||
|
3397: %179 = sext i32 %178 to i64
|
||||||
|
3398: %180 = getelementptr inbounds %struct.MSSFaceThinPoint, %struct.MSSFaceThinPoint addrspace(2)* %1, i64 %179, i32 0
|
||||||
|
3399: %181 = load <2 x float>, <2 x float> addrspace(2)* %180, align 8, !tbaa !74
|
||||||
|
3400: %182 = extractelement <2 x i32> %176, i64 1
|
||||||
|
3401: %183 = add nsw i32 %182, %2
|
||||||
|
3402: %184 = sext i32 %183 to i64
|
||||||
|
3403: %185 = getelementptr inbounds %struct.MSSFaceThinPoint, %struct.MSSFaceThinPoint addrspace(2)* %1, i64 %184, i32 0
|
||||||
|
3404: %186 = load <2 x float>, <2 x float> addrspace(2)* %185, align 8, !tbaa !74
|
||||||
|
3405: %187 = fsub fast <2 x float> %181, %186
|
||||||
|
3406: %188 = tail call fast float @air.dot.v2f32(<2 x float> %187, <2 x float> %187) #2
|
||||||
|
3407: %189 = tail call fast float @air.fast_sqrt.f32(float %188) #2
|
||||||
|
3408: %190 = fcmp fast ugt float %189, 0x3F1A36E2E0000000
|
||||||
|
3409: br i1 %190, label %191, label %205
|
||||||
|
3410:
|
||||||
|
3411: 191: ; preds = %171
|
||||||
|
3412: %192 = fsub fast <2 x float> %186, %181
|
||||||
|
3413: %193 = fsub fast <2 x float> %172, %181
|
||||||
|
3414: %194 = tail call fast float @air.dot.v2f32(<2 x float> %193, <2 x float> %193) #2
|
||||||
|
3415: %195 = tail call fast float @air.fast_sqrt.f32(float %194) #2
|
||||||
|
3416: %196 = fdiv fast float %195, %189
|
||||||
|
3417: %197 = fsub fast float 1.000000e+00, %196
|
||||||
|
3418: %198 = tail call fast float @air.fast_clamp.f32(float %197, float 0.000000e+00, float 1.000000e+00) #2
|
||||||
|
3419: %199 = insertelement <2 x float> undef, float %198, i64 0
|
||||||
|
3420: %200 = shufflevector <2 x float> %199, <2 x float> undef, <2 x i32> zeroinitializer
|
||||||
|
3421: %201 = fmul fast <2 x float> %192, %169
|
||||||
|
3422: %202 = fmul fast <2 x float> %201, %200
|
||||||
|
3423: %203 = fsub fast <2 x float> %172, %202
|
||||||
|
3424: %204 = tail call fast <2 x float> @air.fast_clamp.v2f32(<2 x float> %203, <2 x float> zeroinitializer, <2 x float> <float 1.000000e+00, float 1.000000e+00>) #2
|
||||||
|
3425: br label %205
|
||||||
|
3426:
|
||||||
|
3427: 205: ; preds = %191, %171
|
||||||
|
3428: %206 = phi <2 x float> [ %204, %191 ], [ %172, %171 ]
|
||||||
|
3429: %207 = add nuw nsw i32 %173, 1
|
||||||
|
3430: %208 = icmp eq i32 %207, 4
|
||||||
|
3431: br i1 %208, label %170, label %171, !llvm.loop !89
|
||||||
@@ -0,0 +1,258 @@
|
|||||||
|
{
|
||||||
|
"authority": "Static shipped Apple AIR and Mach-O byte/assembly inspection; target app and shaders not executed",
|
||||||
|
"sources": {
|
||||||
|
"metallib": "/Users/dayu/Downloads/MSE18V2.app/Contents/PlugIns/MSE18V2.pluginkit/Contents/Resources/default.metallib",
|
||||||
|
"metallib_sha256": "856849c45cea2c8f083eeba06a9e318c450f4a56694b438d1a5ba8ef752a0ee0",
|
||||||
|
"command": [
|
||||||
|
"xcrun",
|
||||||
|
"metal-objdump",
|
||||||
|
"--metallib",
|
||||||
|
"--disassemble",
|
||||||
|
"/Users/dayu/Downloads/MSE18V2.app/Contents/PlugIns/MSE18V2.pluginkit/Contents/Resources/default.metallib"
|
||||||
|
],
|
||||||
|
"air_sha256": "1d5bd8d5a3917afbb3b4fe1c40b44480760614102685ac826e84ad2e30a450f9",
|
||||||
|
"binary": "/Users/dayu/Downloads/MSE18V2.app/Contents/PlugIns/MSE18V2.pluginkit/Contents/MacOS/MSE18V2",
|
||||||
|
"binary_arm64_sha256": "afc71d0d21fd3c1ca95ca745ddafcd729e36ad95f17d8638009f2e45d9b7e8df",
|
||||||
|
"double065_file_offsets_in_arm64_slice": [
|
||||||
|
548512
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"new_version_vface": {
|
||||||
|
"struct_field": "MSSFaceShapeParams.vlian offset8; helper parameter5",
|
||||||
|
"render_params_field": "faceV double offset0x90",
|
||||||
|
"cpu_function": "0x10002fb74",
|
||||||
|
"cpu_coefficient_address": "0x100085ea0",
|
||||||
|
"cpu_coefficient": 0.065,
|
||||||
|
"cpu_ratio": "float(clamp(faceV,0,1)*0.065*double(float(clamp(commonStrength,0,1))))",
|
||||||
|
"pin_order": [
|
||||||
|
[
|
||||||
|
69,
|
||||||
|
39
|
||||||
|
],
|
||||||
|
[
|
||||||
|
65,
|
||||||
|
35
|
||||||
|
],
|
||||||
|
[
|
||||||
|
70,
|
||||||
|
26
|
||||||
|
],
|
||||||
|
[
|
||||||
|
64,
|
||||||
|
34
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"pin_fields": [
|
||||||
|
"origin",
|
||||||
|
"direction/radius anchor"
|
||||||
|
],
|
||||||
|
"coordinate_system": "full-image normalized UV; q and points must share the same vertical axis",
|
||||||
|
"radius": "length(Panchor-Porigin), unscaled UV metric; skip radius<=0.0001",
|
||||||
|
"weight": "clamp(1-length(q-Porigin)/radius,0,1)",
|
||||||
|
"step": "q=clamp(q-(Panchor-Porigin)*ratio*weight,0,1)",
|
||||||
|
"sequential": true,
|
||||||
|
"ratio_threshold": 1e-06,
|
||||||
|
"shader_lines": {
|
||||||
|
"helper": 3159,
|
||||||
|
"vface_gate": 3364,
|
||||||
|
"pins": [
|
||||||
|
3374,
|
||||||
|
3376,
|
||||||
|
3378,
|
||||||
|
3380
|
||||||
|
],
|
||||||
|
"loop": 3389,
|
||||||
|
"radius": 3405,
|
||||||
|
"offset": 3412,
|
||||||
|
"clamp": 3424,
|
||||||
|
"count": 3430,
|
||||||
|
"metadata": 4132
|
||||||
|
},
|
||||||
|
"order": "thin17 -> narrow16 -> V4 -> jaw6 -> cheek6 -> chin3 -> short -> smallHead -> bigEye",
|
||||||
|
"helper_copies": [
|
||||||
|
{
|
||||||
|
"start_line": 3159,
|
||||||
|
"v_pins": [
|
||||||
|
[
|
||||||
|
69,
|
||||||
|
39
|
||||||
|
],
|
||||||
|
[
|
||||||
|
65,
|
||||||
|
35
|
||||||
|
],
|
||||||
|
[
|
||||||
|
70,
|
||||||
|
26
|
||||||
|
],
|
||||||
|
[
|
||||||
|
64,
|
||||||
|
34
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"has_old_pixel_gain_constant": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"start_line": 6840,
|
||||||
|
"v_pins": [
|
||||||
|
[
|
||||||
|
69,
|
||||||
|
39
|
||||||
|
],
|
||||||
|
[
|
||||||
|
65,
|
||||||
|
35
|
||||||
|
],
|
||||||
|
[
|
||||||
|
70,
|
||||||
|
26
|
||||||
|
],
|
||||||
|
[
|
||||||
|
64,
|
||||||
|
34
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"has_old_pixel_gain_constant": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"start_line": 8682,
|
||||||
|
"v_pins": [
|
||||||
|
[
|
||||||
|
69,
|
||||||
|
39
|
||||||
|
],
|
||||||
|
[
|
||||||
|
65,
|
||||||
|
35
|
||||||
|
],
|
||||||
|
[
|
||||||
|
70,
|
||||||
|
26
|
||||||
|
],
|
||||||
|
[
|
||||||
|
64,
|
||||||
|
34
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"has_old_pixel_gain_constant": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"version_difference": {
|
||||||
|
"old_air": "build/pose-analysis/face-thin.air.txt",
|
||||||
|
"old_gain_lines": [
|
||||||
|
592,
|
||||||
|
633
|
||||||
|
],
|
||||||
|
"old_vface_lines": [
|
||||||
|
830,
|
||||||
|
897
|
||||||
|
],
|
||||||
|
"old_cpu_coefficient_address": "0x10005f9a0",
|
||||||
|
"coefficient_unchanged": true,
|
||||||
|
"old_extra_gain": "clamp(280/max(length((P75-P59)*pixelSize),2*length((P67-(P74+P60)/2)*pixelSize),1),1,2.25)",
|
||||||
|
"new_extra_gain": "Absent from all3 new shader helper copies and the complete new AIR text; CPU helper is plain clamped-control*.065*commonStrength",
|
||||||
|
"anatomy": "Origin indices64,65,69,70 are lower contour; anchor indices26,34,35,39 are outer lips; eyes0..13 are not controls."
|
||||||
|
},
|
||||||
|
"eye_support_probe": {
|
||||||
|
"authority": "Derived from existing local public-API Vision probe; no new image coordinates or original plug-in execution",
|
||||||
|
"settings": {
|
||||||
|
"vface": 1,
|
||||||
|
"common_strength": 1,
|
||||||
|
"coefficient": 0.065,
|
||||||
|
"pins": [
|
||||||
|
[
|
||||||
|
69,
|
||||||
|
39
|
||||||
|
],
|
||||||
|
[
|
||||||
|
65,
|
||||||
|
35
|
||||||
|
],
|
||||||
|
[
|
||||||
|
70,
|
||||||
|
26
|
||||||
|
],
|
||||||
|
[
|
||||||
|
64,
|
||||||
|
34
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"samples": [
|
||||||
|
{
|
||||||
|
"seconds": 0,
|
||||||
|
"minimum_eye_distance_over_pin_radius": 2.86112178668839,
|
||||||
|
"max_eye_weight": 0,
|
||||||
|
"max_sequential_eye_displacement_pixels": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"seconds": 2,
|
||||||
|
"minimum_eye_distance_over_pin_radius": 2.7386373955689196,
|
||||||
|
"max_eye_weight": 0,
|
||||||
|
"max_sequential_eye_displacement_pixels": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"seconds": 4,
|
||||||
|
"minimum_eye_distance_over_pin_radius": 2.0723834235969316,
|
||||||
|
"max_eye_weight": 0,
|
||||||
|
"max_sequential_eye_displacement_pixels": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"seconds": 6,
|
||||||
|
"minimum_eye_distance_over_pin_radius": 2.7994186406360964,
|
||||||
|
"max_eye_weight": 0,
|
||||||
|
"max_sequential_eye_displacement_pixels": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"seconds": 8,
|
||||||
|
"minimum_eye_distance_over_pin_radius": 2.720538093620307,
|
||||||
|
"max_eye_weight": 0,
|
||||||
|
"max_sequential_eye_displacement_pixels": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"seconds": 10,
|
||||||
|
"minimum_eye_distance_over_pin_radius": 2.7459899006401245,
|
||||||
|
"max_eye_weight": 0,
|
||||||
|
"max_sequential_eye_displacement_pixels": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"seconds": 12,
|
||||||
|
"minimum_eye_distance_over_pin_radius": 2.8522571566241655,
|
||||||
|
"max_eye_weight": 0,
|
||||||
|
"max_sequential_eye_displacement_pixels": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"seconds": 14,
|
||||||
|
"minimum_eye_distance_over_pin_radius": 2.7849986024852744,
|
||||||
|
"max_eye_weight": 0,
|
||||||
|
"max_sequential_eye_displacement_pixels": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"seconds": 16,
|
||||||
|
"minimum_eye_distance_over_pin_radius": 2.86097776991491,
|
||||||
|
"max_eye_weight": 0,
|
||||||
|
"max_sequential_eye_displacement_pixels": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"seconds": 18,
|
||||||
|
"minimum_eye_distance_over_pin_radius": 2.4994391012873294,
|
||||||
|
"max_eye_weight": 0,
|
||||||
|
"max_sequential_eye_displacement_pixels": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"seconds": 20,
|
||||||
|
"minimum_eye_distance_over_pin_radius": 2.3076941598447127,
|
||||||
|
"max_eye_weight": 0,
|
||||||
|
"max_sequential_eye_displacement_pixels": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"limits": [
|
||||||
|
"No reference shader runtime or pixel comparison performed.",
|
||||||
|
"The commonStrength caller-source semantics are not fully traced; formula reports the helper input.",
|
||||||
|
"CPU prechecks/license/module gates are observed but are outside the independently authored geometry reconstruction.",
|
||||||
|
"Eye support results apply to the11 existing saved Vision samples, not every possible pose.",
|
||||||
|
"No product sources, app build, host session or Git were changed."
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# 2.4.0 面板参数迁移
|
||||||
|
|
||||||
|
2026-10-22(Asia/Shanghai)。用户通过截图纠正原需求:迁移新版面板参数,并非新增身体瘦身。此前[瘦身目标核对](瘦身迁移目标核对.md)保留为历史调查;本次继续工作使用 `codex/new-beauty-controls`,不再等待其他目标包。
|
||||||
|
|
||||||
|
## 参考与证据
|
||||||
|
|
||||||
|
目标:`/Users/dayu/Downloads/MSE18V2.app`,2.4.0 / 20260724150000;插件通用二进制 SHA-256:`577303e0de72e07feb576c882e82382b3f056b023a7e987d990ffb37f6b16109`;arm64 切片:`afc71d0d21fd3c1ca95ca745ddafcd729e36ad95f17d8638009f2e45d9b7e8df`。分析使用 Ghidra 12.1.4 与只读 Mach-O 元数据/反汇编,原文件未改。
|
||||||
|
|
||||||
|
| 参考面板 | 目标字段/转换 | 本项目入口 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 磨皮程度 | ID12 → midtoneDetail,直接 0–100% | 保留 PGMidtone 12,调整名称 |
|
||||||
|
| 微细锐化 | ID4 → sharpness,局部细节恢复 | 新 PGMicroDetail 77;原全局锐化 4 留在更多设置 |
|
||||||
|
| 五官保护 | ID30 → featureProtectionRelease = 1 − UI;旧 bool 随 release 接近 1 而关闭 | 新 PGFeatureProtectionAmount 75,直接保护量;旧 bool 30 保留类型与读取 |
|
||||||
|
| 非肤色区保护 | ID17 → highPassDetail = 1 − UI;新主路径用于肤色蒙版扩展 | 新 PGNonSkinProtection 76;原独立全局磨皮 17 留在更多设置 |
|
||||||
|
| 白牙、检测框预览、多人及脸型 | 截图中的这些项已经存在 | 保留;检测框名称调整、多人模式增加提示 |
|
||||||
|
|
||||||
|
两个新保护量默认为 100%,微细锐化默认为 0%。内部使用直接保护量,不直接复用参考包的反向 release 数值。
|
||||||
|
|
||||||
|
`setFloatValue:parameterID:minValue:maxValue:`(`0x1000116d8`)分别反转 17/30 的输入;Evidence:`ev_1fb7d2361dabf814edd6f22d217265b63efa9af24739741772ad5f579e0c67e3`。`valuesForControlPanelValue:` 返回相应原始字段;Evidence:`ev_20da8abf4eb5101b0902627255c643a783b7a15337aa4a757820ab71616f33e7`。显示格式 `strength` 是直接乘 100,只有 `protectionInverse` 格式自身反转;17/30 在绘制与交互同步时提前反转,不能把所有 strength 控制都解释为反向。格式函数汇编 Evidence:`ev_8e2a143f81d173c7643ecee2f1dac4a02145edbf82f113c8a48271428baf747c`。
|
||||||
|
|
||||||
|
目标原生处理入口 `applySkinSmoothingToImage:…`(`0x1000358d8`)把 params+0x50 用于非肤色参与,把 params+0x48 用于五官保护释放。Evidence:`ev_34eb4fe26dbaed9fc37d664d9363db903339c1447b6cd7fdb1980dcd61e99572`。可读 CI 内核字符串建立两个关键关系:肤色蒙版向白色线性扩展;保护蒙版与未保护蒙版按 release 线性混合。细节恢复使用原画与低频图的差,并受局部蒙版和亮度/纹理门控。字符串证据:`ev_d02bfe8046953e9287bd46c5403e6585354a5aa6df782a775d6a6fd0d96f1d62`。这里只记录公式和含义,不提交目标模型、metallib 或资源。
|
||||||
|
|
||||||
|
## 实施与兼容
|
||||||
|
|
||||||
|
本项目继续使用自研 Vision 几何蒙版与 Core Image 管线。新的平滑支持蒙版通过现有未保护/已保护覆盖率恢复五官衰减比例,再对非肤色区域扩展;两个控制独立于曝光/肤色蒙版。默认 100%/100% 直接沿用原覆盖路径。微细锐化在局部支持区域恢复亮度细节与少量色彩细节,可在磨皮量为零时独立使用。
|
||||||
|
|
||||||
|
原目标的语义蒙版、特征形状、纹理处理和复杂组合尚未全链对齐,因此本次是参数含义、连续控制和对应局部处理的迁移,不表示整个新版已逐像素复刻。旧版全局磨皮/全局锐化仍可通过更多设置使用,不与新保护项偷换含义;历史工程保留原强度。
|
||||||
|
|
||||||
|
PGBeautySettings v2 尾部增加三个 float,保留 v1 的 160 字节前缀。解码只接受 v1+160 字节或 v2+新完整长度;旧状态补中性默认值。PGInspectorState 仍使用 schema 1 数值字典,旧 bool 30 迁移到缺失的 75,新数值存在时优先采用新值。面板提交先保存权威对象 210,成功后再镜像旧 bool;每次拖动步进仍使用独立宿主 action。
|
||||||
|
|
||||||
|
模板继续发布原 210 对象与两项署名。新隐藏参数由插件注册和缺省值补齐,原 `.moef` 字节未改,无须覆盖用户模板。固定 Bundle ID、滤镜 UUID、安装路径及宣传资源不变。
|
||||||
|
|
||||||
|
## 验证
|
||||||
|
|
||||||
|
最终通过 1636 项:Beauty 57、Face 116、几何 796、宿主适配 ASan 341、OSC ASan 98、Inspector ASan 228(其中 1 项为可选离屏 PNG 输出)。新增检查包含保护量 0/50/100 的像素响应、非肤色保护只改变局部磨皮支持、微细锐化区域、颜色覆盖隔离、v1 状态迁移与错误长度拒绝、新滑杆归档/插值/输入/重置/旁路/主开关、松手前中间状态发布。离屏 AppKit 面板在多种宽度检查;原生宿主视觉验收需另记实际结果。
|
||||||
|
|
||||||
|
完整 REA 证据仅在忽略的 `build/body-analysis/new-controls-evidence.json`。本文保留目标身份、入口、公式与边界,恢复构建不依赖该本地调查目录。
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
核对日期:2026-10-22(Asia/Shanghai)。开发分支:`codex/body-slimming`;源码基线:`13821366a5baee9beea40972118d7fe634b5c735`。
|
核对日期:2026-10-22(Asia/Shanghai)。开发分支:`codex/body-slimming`;源码基线:`13821366a5baee9beea40972118d7fe634b5c735`。
|
||||||
|
|
||||||
|
**后续已澄清**:用户通过截图确认记错了,实际需求是迁移新增面板参数。后续以[新版磨皮参数迁移](新版磨皮参数迁移.md)为准,下面保留纠正前的有界调查结论,不再表示当前等待其他插件包。
|
||||||
|
|
||||||
## 当前结果
|
## 当前结果
|
||||||
|
|
||||||
用户指定的新目标为 `/Users/dayu/Downloads/MSE18V2.app`。已读取项目交接并检查目标包,**尚未定位到可迁移的新增“瘦身”板块**。当前只完成目标核对与接入点调查,未修改产品源码、模板或正式安装的 1.2.0 APP,未编译或执行目标程序。
|
用户指定的新目标为 `/Users/dayu/Downloads/MSE18V2.app`。已读取项目交接并检查目标包,**尚未定位到可迁移的新增“瘦身”板块**。当前只完成目标核对与接入点调查,未修改产品源码、模板或正式安装的 1.2.0 APP,未编译或执行目标程序。
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
{
|
||||||
|
"repositoryId": 1428817365,
|
||||||
|
"oldName": "FAMEDaYu/FCP-BeatyGril",
|
||||||
|
"newName": "FAMEDaYu/FCP-BeautyGirl",
|
||||||
|
"url": "https://github.com/FAMEDaYu/FCP-BeautyGirl",
|
||||||
|
"baselineCommit": "cd2e608ec6bb92bb6bb0923e4fb4b7728404c67a",
|
||||||
|
"allBranchAndTagRefsUnchanged": true,
|
||||||
|
"releaseAndAssetIdsUnchanged": true,
|
||||||
|
"githubRemote": "https://github.com/FAMEDaYu/FCP-BeautyGirl.git",
|
||||||
|
"sourceProductUnchanged": true,
|
||||||
|
"date": "2026-10-22",
|
||||||
|
"links": {
|
||||||
|
"redirects": [
|
||||||
|
{
|
||||||
|
"old": "https://github.com/FAMEDaYu/FCP-BeatyGril",
|
||||||
|
"new": "https://github.com/FAMEDaYu/FCP-BeautyGirl",
|
||||||
|
"redirectCode": 301,
|
||||||
|
"newURLStatus": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"old": "https://github.com/FAMEDaYu/FCP-BeatyGril/releases/tag/v1.3.1",
|
||||||
|
"new": "https://github.com/FAMEDaYu/FCP-BeautyGirl/releases/tag/v1.3.1",
|
||||||
|
"redirectCode": 301,
|
||||||
|
"newURLStatus": 200
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"articleCanonicalLinksVerified": true
|
||||||
|
},
|
||||||
|
"globalRepositoryIndex": {
|
||||||
|
"remote": "https://dayu.media/git/dayu/codex-global-rules.git",
|
||||||
|
"baseline": "8d3558429efd22af8bad49c03060da326cb231d3",
|
||||||
|
"commit": "750b430b75e4ccdf5badae8ee3f88a07144b3a9e",
|
||||||
|
"trackedFilesCompared": 11,
|
||||||
|
"independentCloneAndFsck": true,
|
||||||
|
"changedFile": "REPOSITORIES.md"
|
||||||
|
},
|
||||||
|
"githubReleaseNotes": [
|
||||||
|
{
|
||||||
|
"id": 420500792,
|
||||||
|
"tag": "v1.3.1",
|
||||||
|
"assetsUnchanged": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dayuReleaseNotes": [
|
||||||
|
{
|
||||||
|
"id": 136,
|
||||||
|
"tag": "v1.3.1",
|
||||||
|
"assetsUnchanged": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"article": {
|
||||||
|
"id": 15593,
|
||||||
|
"url": "https://dayu.media/15593/",
|
||||||
|
"change": "GitHub repository name/URL only",
|
||||||
|
"downloadCoverSeoAndAccessPreserved": true
|
||||||
|
},
|
||||||
|
"immutableArtifacts": "Existing version tags, notarized APP/PKG and published source ZIP bytes retained; no rebuild or installer upload."
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"remote": "https://github.com/FAMEDaYu/FCP-BeautyGirl.git",
|
||||||
|
"commit": "6a29d52a9ad667665da4acf75017c008fa0a566d",
|
||||||
|
"anonymousClone": true,
|
||||||
|
"trackedFilesCompared": 239,
|
||||||
|
"bytesAndExecutableModesMatched": true,
|
||||||
|
"gitFsckFullStrict": true,
|
||||||
|
"productSourceUnchangedFrom": "cd2e608ec6bb92bb6bb0923e4fb4b7728404c67a",
|
||||||
|
"lfs": false,
|
||||||
|
"submodules": false,
|
||||||
|
"temporaryCloneRemoved": true
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
{
|
||||||
|
"oldLogin": "FAMEDaYu",
|
||||||
|
"newLogin": "dayumeida",
|
||||||
|
"accountId": 117798680,
|
||||||
|
"profileURL": "https://github.com/dayumeida",
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"id": 1428817365,
|
||||||
|
"oldName": "FAMEDaYu/FCP-BeautyGirl",
|
||||||
|
"newName": "dayumeida/FCP-BeautyGirl",
|
||||||
|
"allRefsUnchanged": true,
|
||||||
|
"releasesAndAssetsUnchanged": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 904215594,
|
||||||
|
"oldName": "FAMEDaYu/FCPX-",
|
||||||
|
"newName": "dayumeida/FCPX-",
|
||||||
|
"allRefsUnchanged": true,
|
||||||
|
"releasesAndAssetsUnchanged": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"localGithubRemote": "https://github.com/dayumeida/FCP-BeautyGirl.git",
|
||||||
|
"date": "2026-10-22",
|
||||||
|
"links": {
|
||||||
|
"redirects": [
|
||||||
|
{
|
||||||
|
"old": "https://github.com/FAMEDaYu/FCP-BeautyGirl",
|
||||||
|
"final": "https://github.com/dayumeida/FCP-BeautyGirl",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"old": "https://github.com/FAMEDaYu/FCP-BeatyGril",
|
||||||
|
"final": "https://github.com/dayumeida/FCP-BeautyGirl",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"old": "https://github.com/FAMEDaYu/FCP-BeautyGirl/releases/tag/v1.3.1",
|
||||||
|
"final": "https://github.com/dayumeida/FCP-BeautyGirl/releases/tag/v1.3.1",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"old": "https://github.com/FAMEDaYu/FCPX-",
|
||||||
|
"final": "https://github.com/dayumeida/FCPX-",
|
||||||
|
"status": 200
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"newProfileStatus": 200,
|
||||||
|
"articleLinksVerified": true
|
||||||
|
},
|
||||||
|
"globalRepositoryIndex": {
|
||||||
|
"remote": "https://dayu.media/git/dayu/codex-global-rules.git",
|
||||||
|
"baseline": "750b430b75e4ccdf5badae8ee3f88a07144b3a9e",
|
||||||
|
"commit": "dde7e2ee26eae18627eefda9b956a6aca52354ad",
|
||||||
|
"trackedFilesCompared": 11,
|
||||||
|
"independentCloneAndFsck": true,
|
||||||
|
"changedFile": "REPOSITORIES.md"
|
||||||
|
},
|
||||||
|
"githubReleaseNotes": [
|
||||||
|
{
|
||||||
|
"id": 420500792,
|
||||||
|
"tag": "v1.3.1",
|
||||||
|
"assetsUnchanged": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dayuReleaseNotes": [
|
||||||
|
{
|
||||||
|
"id": 136,
|
||||||
|
"tag": "v1.3.1",
|
||||||
|
"assetsUnchanged": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"article": {
|
||||||
|
"id": 15593,
|
||||||
|
"url": "https://dayu.media/15593/",
|
||||||
|
"change": "GitHub owner URL only",
|
||||||
|
"downloadCoverSeoAndAccessPreserved": true
|
||||||
|
},
|
||||||
|
"sourceArtifacts": "No change to product source or APP/PKG/source ZIP bytes. Historical verification evidence retains the prior username."
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"remote": "https://github.com/dayumeida/FCP-BeautyGirl.git",
|
||||||
|
"commit": "2faf7de77929cf7707645ebb38095b03d5729647",
|
||||||
|
"anonymousClone": true,
|
||||||
|
"trackedFilesCompared": 241,
|
||||||
|
"bytesAndExecutableModesMatched": true,
|
||||||
|
"gitFsckFullStrict": true,
|
||||||
|
"productSourceUnchangedFrom": "e47736222d3cbf5edadab26b2a28c82987327043",
|
||||||
|
"lfs": false,
|
||||||
|
"submodules": false,
|
||||||
|
"temporaryCloneRemoved": true
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"date": "2026-10-22",
|
||||||
|
"timezone": "Asia/Shanghai",
|
||||||
|
"version": "1.3.0",
|
||||||
|
"build": "15",
|
||||||
|
"status": "passed",
|
||||||
|
"checks": {
|
||||||
|
"beauty": 57,
|
||||||
|
"face": 116,
|
||||||
|
"geometry": 796,
|
||||||
|
"host_adapter_asan": 341,
|
||||||
|
"osc_asan": 98,
|
||||||
|
"inspector_asan_including_optional_preview": 228
|
||||||
|
},
|
||||||
|
"total": 1636,
|
||||||
|
"commands": [
|
||||||
|
"./scripts/test.sh",
|
||||||
|
"PAINTFCP_HOST_ASAN=1 ./scripts/test-host.sh",
|
||||||
|
"PAINTFCP_OSC_ASAN=1 ./scripts/test-osc.sh",
|
||||||
|
"PAINTFCP_INSPECTOR_ASAN=1 PAINTFCP_INSPECTOR_PNG=<local-path> ./scripts/test-inspector.sh"
|
||||||
|
],
|
||||||
|
"notes": [
|
||||||
|
"OSC passed before the later standalone beauty fallback and legacy inspector numeric-read fixes; those fixes do not change OSC code.",
|
||||||
|
"Core, host adapter and inspector rerun after all source fixes.",
|
||||||
|
"Inspector includes one optional offscreen PNG write assertion.",
|
||||||
|
"No native Motion/FCP interaction test performed for this version; active user hosts are preserved.",
|
||||||
|
"Reference interpretation and implementation limitations are in docs/analysis/新版磨皮参数迁移.md."
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -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": "2f2c081980dc610ac810f8daf92da8966f310802",
|
||||||
|
"trackedWorktreeChanges": []
|
||||||
|
},
|
||||||
|
"updatedAt": "2026-10-22T07:15:35.383726+00:00",
|
||||||
|
"version": "1.3.0",
|
||||||
|
"build": "15",
|
||||||
|
"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=93e3245cafc8ced5f2c01b8a72c8e0c3933eee83\nCandidateCDHashFull sha256=93e3245cafc8ced5f2c01b8a72c8e0c3933eee837831a2d779233e4364dded7c\nHash choices=sha256\nCMSDigest=93e3245cafc8ced5f2c01b8a72c8e0c3933eee837831a2d779233e4364dded7c\nCMSDigestType=2\nCDHash=93e3245cafc8ced5f2c01b8a72c8e0c3933eee83\nSignature size=8968\nAuthority=Developer ID Application: zhong luo (B3W25FX5H5)\nAuthority=Developer ID Certification Authority\nAuthority=Apple Root CA\nTimestamp=Oct 22, 2026 at 15:07:35\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": {
|
||||||
|
"id": "ca96837f-bf33-44fa-8d58-799026ce5c7d",
|
||||||
|
"message": "Processing complete",
|
||||||
|
"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=93e3245cafc8ced5f2c01b8a72c8e0c3933eee83\nCandidateCDHashFull sha256=93e3245cafc8ced5f2c01b8a72c8e0c3933eee837831a2d779233e4364dded7c\nHash choices=sha256\nCMSDigest=93e3245cafc8ced5f2c01b8a72c8e0c3933eee837831a2d779233e4364dded7c\nCMSDigestType=2\nCDHash=93e3245cafc8ced5f2c01b8a72c8e0c3933eee83\nSignature size=8968\nAuthority=Developer ID Application: zhong luo (B3W25FX5H5)\nAuthority=Developer ID Certification Authority\nAuthority=Apple Root CA\nTimestamp=Oct 22, 2026 at 15:07:35\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)\t97262612-DD61-47F2-B25E-D82B95C893EE\t2026-10-22 07:15:35 +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,38 @@
|
|||||||
|
{
|
||||||
|
"status": "verified",
|
||||||
|
"date": "2026-10-22",
|
||||||
|
"timezone": "Asia/Shanghai",
|
||||||
|
"installedProduct": {
|
||||||
|
"path": "/Library/Plug-Ins/FxPlug/beatygril.app",
|
||||||
|
"version": "1.3.0",
|
||||||
|
"build": "15"
|
||||||
|
},
|
||||||
|
"signatureAllArchitecturesVerified": true,
|
||||||
|
"duplicateInventory": {
|
||||||
|
"scope": "Listed local paths and existing explicit related PaintFCP paths on mounted volumes; Spotlight additionally queried. Not a full scan of every volume.",
|
||||||
|
"roots": [
|
||||||
|
"/Applications",
|
||||||
|
"/Library/Plug-Ins/FxPlug",
|
||||||
|
"/Users/dayu/Library/Plug-Ins/FxPlug",
|
||||||
|
"/Users/dayu/Documents/PaintFCP",
|
||||||
|
"/Users/dayu/Desktop",
|
||||||
|
"/Users/dayu/Downloads",
|
||||||
|
"/Users/dayu/Library/Developer/Xcode/DerivedData",
|
||||||
|
"/tmp/paintfcp-build-20261021"
|
||||||
|
],
|
||||||
|
"products": [
|
||||||
|
{
|
||||||
|
"path": "/Library/Plug-Ins/FxPlug/beatygril.app",
|
||||||
|
"version": "1.3.0",
|
||||||
|
"build": "15"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"errors": []
|
||||||
|
},
|
||||||
|
"temporaryPathsRemoved": [
|
||||||
|
"/tmp/paintfcp-build-20261021",
|
||||||
|
"build/delivery/previous-beatygril.app",
|
||||||
|
"build/delivery/notarization.zip"
|
||||||
|
],
|
||||||
|
"nativeEffectQA": "pending_user"
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"status": "waiting_for_host_exit",
|
||||||
|
"bundleIdentifier": "com.dayu.beatygril",
|
||||||
|
"installationPath": "/Library/Plug-Ins/FxPlug/beatygril.app",
|
||||||
|
"version": "1.3.0",
|
||||||
|
"build": "15",
|
||||||
|
"source": {
|
||||||
|
"commit": "2f2c081980dc610ac810f8daf92da8966f310802",
|
||||||
|
"trackedWorktreeChanges": []
|
||||||
|
},
|
||||||
|
"notarization": {
|
||||||
|
"id": "ca96837f-bf33-44fa-8d58-799026ce5c7d",
|
||||||
|
"message": "Processing complete",
|
||||||
|
"status": "Accepted"
|
||||||
|
},
|
||||||
|
"signingVerified": true,
|
||||||
|
"stapleValidated": true,
|
||||||
|
"stagingPluginUnregistered": true,
|
||||||
|
"nativeEffectQA": "pending_user",
|
||||||
|
"note": "Prepared Release is Accepted and stapled; installation is waiting for existing user hosts to close. The installed product has not been replaced."
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"status": "verified",
|
||||||
|
"date": "2026-10-22",
|
||||||
|
"commit": "2f2c081980dc610ac810f8daf92da8966f310802",
|
||||||
|
"remote": "https://dayu.media/git/dayu/paintfcp-fxplug.git",
|
||||||
|
"branch": "codex/new-beauty-controls",
|
||||||
|
"trackedFilesCompared": 205,
|
||||||
|
"gitFsckFullStrict": true,
|
||||||
|
"lfs": false,
|
||||||
|
"submodules": false,
|
||||||
|
"resources": "All tracked source, template and artwork bytes match; user QA media/signing credentials are external and not required to compile source."
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"url": "https://dayu.media/git/dayu/paintfcp-fxplug/releases/tag/v1.3.1",
|
||||||
|
"tag": "v1.3.1",
|
||||||
|
"uploadAndMetadataReadbackVerified": true,
|
||||||
|
"assets": [
|
||||||
|
{
|
||||||
|
"name": "Dayu-Portrait-1.3.1-source.zip",
|
||||||
|
"bytes": 9682532
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Dayu-Portrait-1.3.1.pkg",
|
||||||
|
"bytes": 5011286
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"assetByteReadback": "Not verified through the browser download gateway: API token request was redirected to site login. GitHub and article asset bytes were independently verified; installer is also stored in Git and restored from the release tag.",
|
||||||
|
"anonymousAccess": "Site Git gateway continues to require login/membership."
|
||||||
|
}
|
||||||
@@ -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": "7fa3298e022946e77dfd8cfba1bb5ff2c2a6361f",
|
||||||
|
"trackedWorktreeChanges": []
|
||||||
|
},
|
||||||
|
"updatedAt": "2026-10-22T08:52:41.382516+00:00",
|
||||||
|
"version": "1.3.1",
|
||||||
|
"build": "16",
|
||||||
|
"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=d3bf91f8c8589b1acc19779f5578cc4f4055d267\nCandidateCDHashFull sha256=d3bf91f8c8589b1acc19779f5578cc4f4055d2678f10767fbb5d935ffea426a6\nHash choices=sha256\nCMSDigest=d3bf91f8c8589b1acc19779f5578cc4f4055d2678f10767fbb5d935ffea426a6\nCMSDigestType=2\nCDHash=d3bf91f8c8589b1acc19779f5578cc4f4055d267\nSignature size=8968\nAuthority=Developer ID Application: zhong luo (B3W25FX5H5)\nAuthority=Developer ID Certification Authority\nAuthority=Apple Root CA\nTimestamp=Oct 22, 2026 at 16:27:24\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": {
|
||||||
|
"message": "Processing complete",
|
||||||
|
"status": "Accepted",
|
||||||
|
"id": "e353b701-2fc2-4fcc-b03e-69af7735400f"
|
||||||
|
},
|
||||||
|
"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=d3bf91f8c8589b1acc19779f5578cc4f4055d267\nCandidateCDHashFull sha256=d3bf91f8c8589b1acc19779f5578cc4f4055d2678f10767fbb5d935ffea426a6\nHash choices=sha256\nCMSDigest=d3bf91f8c8589b1acc19779f5578cc4f4055d2678f10767fbb5d935ffea426a6\nCMSDigestType=2\nCDHash=d3bf91f8c8589b1acc19779f5578cc4f4055d267\nSignature size=8968\nAuthority=Developer ID Application: zhong luo (B3W25FX5H5)\nAuthority=Developer ID Certification Authority\nAuthority=Apple Root CA\nTimestamp=Oct 22, 2026 at 16:27:24\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)\t330605B9-A653-4D3C-BB2D-D87F041A2071\t2026-10-22 08:52:41 +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,13 @@
|
|||||||
|
{
|
||||||
|
"status": "verified",
|
||||||
|
"tag": "v1.3.1",
|
||||||
|
"commit": "b35b4a5d1ca252d2f83c798613560a768274de3c",
|
||||||
|
"anonymousGitHubClone": true,
|
||||||
|
"trackedFilesCompared": 232,
|
||||||
|
"byteAndExecutableModesMatch": true,
|
||||||
|
"gitFsckFullStrict": true,
|
||||||
|
"productMatchesSignedSource": "7fa3298e022946e77dfd8cfba1bb5ff2c2a6361f",
|
||||||
|
"restoredPkgSignatureAndTicketVerified": true,
|
||||||
|
"lfs": false,
|
||||||
|
"submodules": false
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
Beauty renderer: 57 checks, 0 failures (Metal).
|
||||||
|
2026-10-22 16:24:20.836 face-tests[22582:8025448] FBBA: creating VNFaceBBoxAligner from VNFaceDetectorRevision2: VNFaceDetectorRevision2
|
||||||
|
Face processing: 116 checks passed
|
||||||
|
2026-10-22 16:24:34.977 slim-tests[22589:8025534] FBBA: creating VNFaceBBoxAligner from VNFaceDetectorRevision2: VNFaceDetectorRevision2
|
||||||
|
PASS: 1033 slim alignment checks; maximum UV oracle error 1.09828022e-05
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
2026-10-22 16:21:55.726 host-tests[22385:8017721] 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
|
||||||
|
Anatomy v-face 0/0 max=1 mean=0.0001240 eye=0/0 chin=39246 inverted=0
|
||||||
|
Anatomy v-face 0/1 max=1 mean=0.0001240 eye=0/0 chin=39246 inverted=0
|
||||||
|
Anatomy v-face 1/0 max=1 mean=0.0001240 eye=0/0 chin=39246 inverted=0
|
||||||
|
Anatomy v-face 1/1 max=1 mean=0.0001240 eye=0/0 chin=39246 inverted=0
|
||||||
|
Legacy fallback upright comparison: max=2 mean=0.0010357
|
||||||
|
Host adapter: 370 checks, 0 failures
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"url": "https://github.com/FAMEDaYu/FCP-BeatyGril/releases/tag/v1.3.1",
|
||||||
|
"tag": "v1.3.1",
|
||||||
|
"assets": [
|
||||||
|
{
|
||||||
|
"name": "Dayu-Portrait-1.3.1-source.zip",
|
||||||
|
"bytes": 9682532,
|
||||||
|
"sha256": "7c335f54717a73e710ef484fc9f7b90864211c128a0f217bf170aa092333f228",
|
||||||
|
"anonymousDownloadVerified": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Dayu-Portrait-1.3.1.pkg",
|
||||||
|
"bytes": 5011286,
|
||||||
|
"sha256": "a6bb7262c9066e7abca8d1f21eaeaf1ba6a1d0d51b4859d39307aa7d7b0bc927",
|
||||||
|
"anonymousDownloadVerified": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"installerSignatureAndTicketVerified": true
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Inspector: 227 checks, 0 failures
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"status": "verified",
|
||||||
|
"date": "2026-10-22",
|
||||||
|
"installedProduct": {
|
||||||
|
"path": "/Library/Plug-Ins/FxPlug/beatygril.app",
|
||||||
|
"version": "1.3.1",
|
||||||
|
"build": "16"
|
||||||
|
},
|
||||||
|
"signatureAllArchitecturesVerified": true,
|
||||||
|
"duplicateInventory": {
|
||||||
|
"scope": "Listed local roots only; not a full scan of all mounted volumes.",
|
||||||
|
"roots": [
|
||||||
|
"/Applications",
|
||||||
|
"/Library/Plug-Ins/FxPlug",
|
||||||
|
"/Users/dayu/Library/Plug-Ins/FxPlug",
|
||||||
|
"/Users/dayu/Documents/PaintFCP",
|
||||||
|
"/Users/dayu/Desktop",
|
||||||
|
"/Users/dayu/Downloads",
|
||||||
|
"/Users/dayu/Library/Developer/Xcode/DerivedData",
|
||||||
|
"/tmp/paintfcp-build-20261021"
|
||||||
|
],
|
||||||
|
"products": [
|
||||||
|
{
|
||||||
|
"path": "/Library/Plug-Ins/FxPlug/beatygril.app",
|
||||||
|
"version": "1.3.1",
|
||||||
|
"build": "16"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"errors": []
|
||||||
|
},
|
||||||
|
"temporaryPathsRemoved": [
|
||||||
|
"/tmp/paintfcp-build-20261021",
|
||||||
|
"build/delivery/previous-beatygril.app",
|
||||||
|
"build/delivery/notarization.zip",
|
||||||
|
"build/release-input-1.3.1",
|
||||||
|
"build/pkg-expanded-1.3.1"
|
||||||
|
],
|
||||||
|
"nativeEffectQA": "pending_user",
|
||||||
|
"hostExit": "Motion exited by user; FCP remained running and was quit normally via UI after the user confirmed exit. No project edits."
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
2026-10-22 16:24:26.444 osc-tests[22624:8025751] PGFaceOSC point-write desired=(0.25,0.75) setter=(0.25,0.75) readback=(0.25,0.75) mode=normalized verified=1
|
||||||
|
2026-10-22 16:24:26.452 osc-tests[22624:8025751] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(80,180) surface=320x240 origin=0
|
||||||
|
2026-10-22 16:24:26.887 osc-tests[22624:8025751] PGFaceOSC point-write desired=(0.8,0.3) setter=(1152,-432) readback=(0.8,0.3) mode=centered-pixels verified=1
|
||||||
|
2026-10-22 16:24:26.887 osc-tests[22624:8025751] PGFaceOSC coordinates selection=(0.7,0.4) objectBounds=(-1920,-1080,3840,2160) canvas=(152,78) surface=320x240 origin=0
|
||||||
|
2026-10-22 16:24:26.944 osc-tests[22624:8025751] PGFaceOSC point-write desired=(0.8,0.3) setter=(0.8,0.3) readback=(0.12345,0.87654) mode=normalized verified=0
|
||||||
|
2026-10-22 16:24:26.944 osc-tests[22624:8025751] PGFaceOSC point-write desired=(0.75,0.25) setter=(0.75,0.25) readback=(0.75,0.25) mode=normalized verified=1
|
||||||
|
2026-10-22 16:24:26.944 osc-tests[22624:8025751] PGFaceOSC coordinates selection=(0.75,0.25) objectBounds=(-100,-50,200,100) canvas=(160,60) surface=320x240 origin=0
|
||||||
|
2026-10-22 16:24:26.994 osc-tests[22624:8025751] PGFaceOSC point-write desired=(0.625,0.375) setter=(0.625,0.375) readback=(0.625,0.375) mode=normalized verified=1
|
||||||
|
2026-10-22 16:24:26.994 osc-tests[22624:8025751] PGFaceOSC coordinates selection=(0.625,0.375) objectBounds=(-100,-50,200,100) canvas=(240,60) surface=320x240 origin=0
|
||||||
|
2026-10-22 16:24:27.043 osc-tests[22624:8025751] PGFaceOSC point-write desired=(0.25,0.75) setter=(0.25,0.75) readback=(0.25,0.75) mode=normalized verified=1
|
||||||
|
2026-10-22 16:24:27.043 osc-tests[22624:8025751] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(144,180) surface=320x240 origin=0
|
||||||
|
2026-10-22 16:24:27.094 osc-tests[22624:8025751] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
|
||||||
|
2026-10-22 16:24:27.094 osc-tests[22624:8025751] PGFaceOSC coordinates selection=(0,1) objectBounds=(-100,-50,200,100) canvas=(100,140) surface=320x240 origin=0
|
||||||
|
2026-10-22 16:24:27.144 osc-tests[22624:8025751] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(nan,nan) surface=320x240 origin=0
|
||||||
|
2026-10-22 16:24:27.194 osc-tests[22624:8025751] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
|
||||||
|
2026-10-22 16:24:27.194 osc-tests[22624:8025751] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(inf,nan) surface=320x240 origin=0
|
||||||
|
2026-10-22 16:24:27.242 osc-tests[22624:8025751] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
|
||||||
|
2026-10-22 16:24:27.242 osc-tests[22624:8025751] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(80,nan) surface=320x240 origin=0
|
||||||
|
2026-10-22 16:24:27.304 osc-tests[22624:8025751] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
|
||||||
|
2026-10-22 16:24:27.304 osc-tests[22624:8025751] 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,32 @@
|
|||||||
|
{
|
||||||
|
"status": "verified",
|
||||||
|
"sourceApp": "/Library/Plug-Ins/FxPlug/beatygril.app",
|
||||||
|
"sourceCommit": "7fa3298e022946e77dfd8cfba1bb5ff2c2a6361f",
|
||||||
|
"motionSource": "/Users/dayu/Movies/Motion Templates.localized/Effects.localized/大鱼磨皮瘦脸",
|
||||||
|
"motionFiles": {
|
||||||
|
"大鱼人像美化.moef": "e9fa97dc14323d0daa7d3d61b06a78ba4000a70ab9f5e67ebffd750d49df1e15",
|
||||||
|
"Drop Zone.tiff": "a7f8e3a2a431dbb3ce9e777040bb55e701cfe9ab9632e8cbb10359732bc8692e",
|
||||||
|
"large.png": "a02ddb255b1ae255c749bb837a759e2dee1c3b8ffb9bdc1167957761bd5a1b97",
|
||||||
|
"small.png": "1552b838dd88f60dee0795e20aaca652f9c558ecefc93500e895fbff77c2a4d5"
|
||||||
|
},
|
||||||
|
"lockedFiles": [
|
||||||
|
"大鱼人像美化/large.png",
|
||||||
|
"大鱼人像美化/small.png"
|
||||||
|
],
|
||||||
|
"package": "releases/1.3.1/大鱼人像美化-1.3.1.pkg",
|
||||||
|
"bytes": 5011286,
|
||||||
|
"sha256": "a6bb7262c9066e7abca8d1f21eaeaf1ba6a1d0d51b4859d39307aa7d7b0bc927",
|
||||||
|
"notarization": {
|
||||||
|
"createdDate": "2026-10-22T08:54:34.655Z",
|
||||||
|
"message": "Successfully received submission info",
|
||||||
|
"id": "78105340-306f-4629-8670-772760d4cb9a",
|
||||||
|
"status": "Accepted",
|
||||||
|
"name": "大鱼人像美化-1.3.1.pkg"
|
||||||
|
},
|
||||||
|
"checks": {
|
||||||
|
"installerSignature": "Package \"大鱼人像美化-1.3.1.pkg\":\n Status: signed by a developer certificate issued by Apple for distribution\n Notarization: trusted by the Apple notary service\n Signed with a trusted timestamp on: 2026-10-22 08:54:32 +0000\n Certificate Chain:\n 1. Developer ID Installer: zhong luo (B3W25FX5H5)\n Expires: 2027-02-01 22:12:15 +0000\n SHA256 Fingerprint:\n 4D AC CD 04 A5 4C 7E F8 13 13 A2 ED D1 C2 C0 D0 29 EF FA BF 7B 53 \n 5A 43 C8 72 4D C4 0A A3 94 34\n ------------------------------------------------------------------------\n 2. Developer ID Certification Authority\n Expires: 2027-02-01 22:12:15 +0000\n SHA256 Fingerprint:\n 7A FC 9D 01 A6 2F 03 A2 DE 96 37 93 6D 4A FE 68 09 0D 2D E1 8D 03 \n F2 9C 88 CF B0 B1 BA 63 58 7F\n ------------------------------------------------------------------------\n 3. Apple Root CA\n Expires: 2035-02-09 21:40:36 +0000\n SHA256 Fingerprint:\n B0 B1 73 0E CB C7 FF 45 05 14 2C 49 F1 29 5E 6E DA 6B CA ED 7E 2C \n 68 C5 BE 91 B5 A1 10 01 F0 24\n\n",
|
||||||
|
"ticket": "Processing: /Users/dayu/Documents/PaintFCP/releases/1.3.1/大鱼人像美化-1.3.1.pkg\nThe validate action worked!\n",
|
||||||
|
"gatekeeper": "/Users/dayu/Documents/PaintFCP/releases/1.3.1/大鱼人像美化-1.3.1.pkg: accepted\nsource=Notarized Developer ID\norigin=Developer ID Installer: zhong luo (B3W25FX5H5)\n"
|
||||||
|
},
|
||||||
|
"realInstallerExecuted": false
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
{
|
||||||
|
"logFormatVersion": 1,
|
||||||
|
"jobId": "78105340-306f-4629-8670-772760d4cb9a",
|
||||||
|
"status": "Accepted",
|
||||||
|
"statusSummary": "Ready for distribution",
|
||||||
|
"statusCode": 0,
|
||||||
|
"archiveFilename": "大鱼人像美化-1.3.1.pkg",
|
||||||
|
"uploadDate": "2026-10-22T08:54:36.516Z",
|
||||||
|
"sha256": "e64eda56bedc889bdab4759ad5118beaa83e56aaa92d24c7f96fe993caaa10ff",
|
||||||
|
"ticketContents": [
|
||||||
|
{
|
||||||
|
"path": "大鱼人像美化-1.3.1.pkg/Payload.pkg Contents/Payload/Library/Plug-Ins/FxPlug/beatygril.app",
|
||||||
|
"digestAlgorithm": "SHA-256",
|
||||||
|
"cdhash": "70b387db43f4fd65efc5ebd5f1795f72561dc523",
|
||||||
|
"arch": "x86_64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "大鱼人像美化-1.3.1.pkg/Payload.pkg Contents/Payload/Library/Plug-Ins/FxPlug/beatygril.app",
|
||||||
|
"digestAlgorithm": "SHA-256",
|
||||||
|
"cdhash": "d3bf91f8c8589b1acc19779f5578cc4f4055d267",
|
||||||
|
"arch": "arm64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "大鱼人像美化-1.3.1.pkg/Payload.pkg Contents/Payload/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit",
|
||||||
|
"digestAlgorithm": "SHA-256",
|
||||||
|
"cdhash": "00d0b69489a21f9c4464b38d284a0b8be7fd73c3",
|
||||||
|
"arch": "x86_64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "大鱼人像美化-1.3.1.pkg/Payload.pkg Contents/Payload/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit",
|
||||||
|
"digestAlgorithm": "SHA-256",
|
||||||
|
"cdhash": "8411931c0e2f6fd7b21a168a268b641808cdb558",
|
||||||
|
"arch": "arm64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "大鱼人像美化-1.3.1.pkg/Payload.pkg Contents/Payload/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit/Contents/Frameworks/FxPlug.framework/Versions/Current",
|
||||||
|
"digestAlgorithm": "SHA-256",
|
||||||
|
"cdhash": "54cf6701245852c5c86f0c664fae4ce99f3e9346",
|
||||||
|
"arch": "x86_64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "大鱼人像美化-1.3.1.pkg/Payload.pkg Contents/Payload/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit/Contents/Frameworks/FxPlug.framework/Versions/Current",
|
||||||
|
"digestAlgorithm": "SHA-256",
|
||||||
|
"cdhash": "0eb927b742d37499cd49b950b2c88dca9af31987",
|
||||||
|
"arch": "arm64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "大鱼人像美化-1.3.1.pkg/Payload.pkg Contents/Payload/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit/Contents/Frameworks/PluginManager.framework/Versions/Current",
|
||||||
|
"digestAlgorithm": "SHA-256",
|
||||||
|
"cdhash": "b018110c18ea61de312c47ca584e020d6e307bfd",
|
||||||
|
"arch": "x86_64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "大鱼人像美化-1.3.1.pkg/Payload.pkg Contents/Payload/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit/Contents/Frameworks/PluginManager.framework/Versions/Current",
|
||||||
|
"digestAlgorithm": "SHA-256",
|
||||||
|
"cdhash": "26bcc6c218f15e8d6c96eec52104de495b8de2ff",
|
||||||
|
"arch": "arm64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "大鱼人像美化-1.3.1.pkg/Payload.pkg Contents/Payload/Library/Plug-Ins/FxPlug/beatygril.app/Contents/MacOS/beatygril",
|
||||||
|
"digestAlgorithm": "SHA-256",
|
||||||
|
"cdhash": "70b387db43f4fd65efc5ebd5f1795f72561dc523",
|
||||||
|
"arch": "x86_64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "大鱼人像美化-1.3.1.pkg/Payload.pkg Contents/Payload/Library/Plug-Ins/FxPlug/beatygril.app/Contents/MacOS/beatygril",
|
||||||
|
"digestAlgorithm": "SHA-256",
|
||||||
|
"cdhash": "d3bf91f8c8589b1acc19779f5578cc4f4055d267",
|
||||||
|
"arch": "arm64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "大鱼人像美化-1.3.1.pkg/Payload.pkg Contents/Payload/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit/Contents/MacOS/beatygril XPC Service",
|
||||||
|
"digestAlgorithm": "SHA-256",
|
||||||
|
"cdhash": "00d0b69489a21f9c4464b38d284a0b8be7fd73c3",
|
||||||
|
"arch": "x86_64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "大鱼人像美化-1.3.1.pkg/Payload.pkg Contents/Payload/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit/Contents/MacOS/beatygril XPC Service",
|
||||||
|
"digestAlgorithm": "SHA-256",
|
||||||
|
"cdhash": "8411931c0e2f6fd7b21a168a268b641808cdb558",
|
||||||
|
"arch": "arm64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "大鱼人像美化-1.3.1.pkg/Payload.pkg Contents/Payload/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit/Contents/Frameworks/PluginManager.framework/Versions/B/PluginManager",
|
||||||
|
"digestAlgorithm": "SHA-256",
|
||||||
|
"cdhash": "b018110c18ea61de312c47ca584e020d6e307bfd",
|
||||||
|
"arch": "x86_64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "大鱼人像美化-1.3.1.pkg/Payload.pkg Contents/Payload/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit/Contents/Frameworks/PluginManager.framework/Versions/B/PluginManager",
|
||||||
|
"digestAlgorithm": "SHA-256",
|
||||||
|
"cdhash": "26bcc6c218f15e8d6c96eec52104de495b8de2ff",
|
||||||
|
"arch": "arm64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "大鱼人像美化-1.3.1.pkg/Payload.pkg Contents/Payload/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit/Contents/Frameworks/FxPlug.framework/Versions/A/FxPlug",
|
||||||
|
"digestAlgorithm": "SHA-256",
|
||||||
|
"cdhash": "54cf6701245852c5c86f0c664fae4ce99f3e9346",
|
||||||
|
"arch": "x86_64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "大鱼人像美化-1.3.1.pkg/Payload.pkg Contents/Payload/Library/Plug-Ins/FxPlug/beatygril.app/Contents/PlugIns/beatygril XPC Service.pluginkit/Contents/Frameworks/FxPlug.framework/Versions/A/FxPlug",
|
||||||
|
"digestAlgorithm": "SHA-256",
|
||||||
|
"cdhash": "0eb927b742d37499cd49b950b2c88dca9af31987",
|
||||||
|
"arch": "arm64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "大鱼人像美化-1.3.1.pkg",
|
||||||
|
"digestAlgorithm": "SHA-1",
|
||||||
|
"cdhash": "99c6067f2b58a458edfc534895b160317ba13c65"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"issues": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"package": "releases/1.3.1/大鱼人像美化-1.3.1.pkg",
|
||||||
|
"bytes": 5011286,
|
||||||
|
"sha256": "a6bb7262c9066e7abca8d1f21eaeaf1ba6a1d0d51b4859d39307aa7d7b0bc927",
|
||||||
|
"version": "1.3.1",
|
||||||
|
"build": "16",
|
||||||
|
"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": "f7a1cedbac82d8c7946adc4767d253268bf5e9bbf6aa5b492264f6965092fccb"
|
||||||
|
},
|
||||||
|
"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; formal template files matched the specified installed category, not changed by running this PKG",
|
||||||
|
"fonts": "No editable third-party font dependency; promotional lettering baked into PNG. No optional font ZIP needed."
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
识别到 2 个安装内容:
|
||||||
|
- beatygril.app
|
||||||
|
类型:FxPlug 插件
|
||||||
|
目录:/Library/Plug-Ins/FxPlug
|
||||||
|
状态:已签名并公证,保留隔离属性,自动验证并修复注册;失败时打开 APP 所在目录
|
||||||
|
- 大鱼磨皮瘦脸
|
||||||
|
类型:FCP 效果
|
||||||
|
目录:~/Movies/Motion Templates.localized/Effects.localized
|
||||||
|
状态:非 APP,保留隔离属性
|
||||||
|
|
||||||
|
本次唯一标识:com.dayu.fcppackage.b20261022085430796.tqa79fqb
|
||||||
|
安装包版本:1.3.1(继承 beatygril.app)
|
||||||
|
[正在准备内容]
|
||||||
|
正在整理 2 个内容项
|
||||||
|
[正在生成安装包]
|
||||||
|
正在构建组件包
|
||||||
|
[正在签名]
|
||||||
|
正在使用 Developer ID Installer 签名
|
||||||
|
[正在等待 Apple 公证]
|
||||||
|
已提交 Apple,公证通常需要几分钟
|
||||||
|
[正在粘附公证票据]
|
||||||
|
公证已通过,正在粘附票据
|
||||||
|
[正在验证结果]
|
||||||
|
正在检查签名和安装包结构
|
||||||
|
签名、包结构与公证票据验证通过
|
||||||
|
|
||||||
|
完成:/Users/dayu/Documents/PaintFCP/releases/1.3.1/大鱼人像美化-1.3.1.pkg
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"status": "waiting_for_host_exit",
|
||||||
|
"bundleIdentifier": "com.dayu.beatygril",
|
||||||
|
"installationPath": "/Library/Plug-Ins/FxPlug/beatygril.app",
|
||||||
|
"options": {
|
||||||
|
"waitForHosts": true,
|
||||||
|
"userTests": true
|
||||||
|
},
|
||||||
|
"nativeEffectQA": {
|
||||||
|
"status": "pending_user",
|
||||||
|
"performedByScript": false
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"commit": "7fa3298e022946e77dfd8cfba1bb5ff2c2a6361f",
|
||||||
|
"trackedWorktreeChanges": []
|
||||||
|
},
|
||||||
|
"updatedAt": "2026-10-22T08:27:51.067234+00:00",
|
||||||
|
"version": "1.3.1",
|
||||||
|
"build": "16",
|
||||||
|
"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=d3bf91f8c8589b1acc19779f5578cc4f4055d267\nCandidateCDHashFull sha256=d3bf91f8c8589b1acc19779f5578cc4f4055d2678f10767fbb5d935ffea426a6\nHash choices=sha256\nCMSDigest=d3bf91f8c8589b1acc19779f5578cc4f4055d2678f10767fbb5d935ffea426a6\nCMSDigestType=2\nCDHash=d3bf91f8c8589b1acc19779f5578cc4f4055d267\nSignature size=8968\nAuthority=Developer ID Application: zhong luo (B3W25FX5H5)\nAuthority=Developer ID Certification Authority\nAuthority=Apple Root CA\nTimestamp=Oct 22, 2026 at 16:27:24\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": {
|
||||||
|
"message": "Processing complete",
|
||||||
|
"status": "Accepted",
|
||||||
|
"id": "e353b701-2fc2-4fcc-b03e-69af7735400f"
|
||||||
|
},
|
||||||
|
"signingVerified": true,
|
||||||
|
"stapleValidated": true,
|
||||||
|
"stagingPluginUnregistered": true,
|
||||||
|
"runningHosts": [
|
||||||
|
{
|
||||||
|
"pid": 15637,
|
||||||
|
"executable": "/Applications/Final Cut Pro.app/Contents/MacOS/Final Cut Pro"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pid": 21055,
|
||||||
|
"executable": "/Applications/Motion.app/Contents/MacOS/Motion"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"articleId": 15593,
|
||||||
|
"url": "https://dayu.media/15593/",
|
||||||
|
"status": "publish",
|
||||||
|
"version": "1.3.1",
|
||||||
|
"author": 1,
|
||||||
|
"categories": [
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"featuredMedia": 15595,
|
||||||
|
"free": true,
|
||||||
|
"accessAndCoverPreserved": true,
|
||||||
|
"packageURL": "https://dayu.media/wp-content/uploads/2026/10/1792659519-b628139c96a5fa6.pkg",
|
||||||
|
"packageBytesAndHashVerifiedByPublisher": true,
|
||||||
|
"sourceURL": "https://dayu.media/wp-content/uploads/2026/10/1792659515-d487b0a421aacc6.zip",
|
||||||
|
"frontends": [
|
||||||
|
{
|
||||||
|
"request": "normal URL",
|
||||||
|
"title": "大鱼人像美化 1.3.1|FCP / Motion 免费开源自动美颜插件",
|
||||||
|
"description": "大鱼人像美化是适用于 Final Cut Pro 和 Motion 的免费开源 FxPlug4 插件,1.3.1 修复 V 脸下颌作用区域,并加入微细锐化、五官保护与非肤色区保护。提供 Apple 签名公证 PKG 安装包和源码,安装自动注册,无需激活码,附安装使用及故障排查说明。",
|
||||||
|
"keywords": "大鱼人像美化,FCP美颜插件,Motion磨皮,自动瘦脸,自动跟随,FxPlug4,免费开源",
|
||||||
|
"singleSeoTags": true,
|
||||||
|
"versionAndInstructionsPresent": true,
|
||||||
|
"anonymousPrice": "free; theme requires login to get package"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"request": "fresh query",
|
||||||
|
"title": "大鱼人像美化 1.3.1|FCP / Motion 免费开源自动美颜插件",
|
||||||
|
"description": "大鱼人像美化是适用于 Final Cut Pro 和 Motion 的免费开源 FxPlug4 插件,1.3.1 修复 V 脸下颌作用区域,并加入微细锐化、五官保护与非肤色区保护。提供 Apple 签名公证 PKG 安装包和源码,安装自动注册,无需激活码,附安装使用及故障排查说明。",
|
||||||
|
"keywords": "大鱼人像美化,FCP美颜插件,Motion磨皮,自动瘦脸,自动跟随,FxPlug4,免费开源",
|
||||||
|
"singleSeoTags": true,
|
||||||
|
"versionAndInstructionsPresent": true,
|
||||||
|
"anonymousPrice": "free; theme requires login to get package"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fcpxAppSync": "disabled as before; not requested",
|
||||||
|
"nativeVisualQA": "pending_user"
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
2026-10-22 16:25:35.195 slim-tests[22704:8027100] FBBA: creating VNFaceBBoxAligner from VNFaceDetectorRevision2: VNFaceDetectorRevision2
|
||||||
|
PASS: 1561 slim alignment checks; maximum UV oracle error 1.09828022e-05
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"status": "verified",
|
||||||
|
"date": "2026-10-22",
|
||||||
|
"commit": "7fa3298e022946e77dfd8cfba1bb5ff2c2a6361f",
|
||||||
|
"remote": "https://dayu.media/git/dayu/paintfcp-fxplug.git",
|
||||||
|
"branch": "codex/v-face-jaw-fix",
|
||||||
|
"trackedFilesCompared": 219,
|
||||||
|
"gitFsckFullStrict": true,
|
||||||
|
"xcodeProjectReadable": true,
|
||||||
|
"lfs": false,
|
||||||
|
"submodules": false,
|
||||||
|
"resources": "All tracked bytes and executable modes matched, including source/template/artwork. Private QA inputs and signing credentials are external."
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"status": "passed",
|
||||||
|
"version": "1.3.1",
|
||||||
|
"build": "16",
|
||||||
|
"checks": {
|
||||||
|
"beauty": 57,
|
||||||
|
"face": 116,
|
||||||
|
"geometry": 1033,
|
||||||
|
"hostASan": 370,
|
||||||
|
"oscASan": 98,
|
||||||
|
"inspectorASan": 227
|
||||||
|
},
|
||||||
|
"total": 1901,
|
||||||
|
"extraRealPoseASan": {
|
||||||
|
"checks": 1561,
|
||||||
|
"inputs": "NASA astronaut plus 11 local user frames; frame images and full Vision points not uploaded",
|
||||||
|
"overlapsBaseline": true
|
||||||
|
},
|
||||||
|
"maximumUVOracleError": 1.09828022e-05,
|
||||||
|
"isolatedV": {
|
||||||
|
"eyeChange": [
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"lowerFaceChange": 39246,
|
||||||
|
"originsCompared": 4,
|
||||||
|
"uprightMax255": 1,
|
||||||
|
"uprightMean255": 0.000124
|
||||||
|
},
|
||||||
|
"nativeVisualQA": "pending user"
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"sourceCommit": "b35b4a5d1ca252d2f83c798613560a768274de3c",
|
||||||
|
"tag": "v1.3.1",
|
||||||
|
"files": 230,
|
||||||
|
"bytes": 9682532,
|
||||||
|
"sha256": "7c335f54717a73e710ef484fc9f7b90864211c128a0f217bf170aa092333f228",
|
||||||
|
"excludes": [
|
||||||
|
"releases/**/*.pkg"
|
||||||
|
]
|
||||||
|
}
|
||||||
+25
-13
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
更新时间:2026-10-22,Asia/Shanghai。项目根目录:`/Users/dayu/Documents/PaintFCP`。
|
更新时间:2026-10-22,Asia/Shanghai。项目根目录:`/Users/dayu/Documents/PaintFCP`。
|
||||||
|
|
||||||
当前正式版本为 **1.2.0(构建 14)**,APP 与 PKG 均已完成正式签名、公证和票据验证,APP 已安装,公开源码及文章已发布。没有待完成的发布阻塞项;后续工作应从新的用户反馈或下述已知差异继续,不必重新制作封面、重发文章或重复安装现有版本。
|
当前正式 APP 为 **1.3.1(16)**,通用 Release、正式签名、Apple公证、票据、Gatekeeper和正式路径注册均已核验,临时APP副本已清理。1901项自动回归及1561项重叠真实帧几何ASan通过,V脸和新磨皮参数的最终宿主视觉仍由用户实测。签名源码 `7fa3298e022946e77dfd8cfba1bb5ff2c2a6361f`;本次PKG由本机PKG工坊CLI打包,包含用户指定已安装的「大鱼磨皮瘦脸」分类及4项模板资源。文章15593、GitHub和大鱼Release均已更新1.3.1,main与标签已同步;线上下载和恢复的具体核验边界见本版本回执。
|
||||||
|
|
||||||
代码由 **OpenAI + Codex 全程编写,站长仅参与调试和测试**。文章和仓库须保留这个表述,不使用“站长原创代码”等说法。免费版本无需账号、激活码或付费授权;“自动注册”指系统插件注册。
|
代码由 **OpenAI + Codex 全程编写,站长仅参与调试和测试**。文章和仓库须保留这个表述,不使用“站长原创代码”等说法。免费版本无需账号、激活码或付费授权;“自动注册”指系统插件注册。
|
||||||
|
|
||||||
本交接整理前,工作区干净,`main`、`origin/main` 与 `github/main` 均为 `189a5d7634aa69db54330c90f8711b05d8e44b10`。这是文档新增前的基线;最新文档节点以 `git log -1` 和远端引用为准。
|
1.2.0 交接整理前,工作区干净,`main`、`origin/main` 与 `github/main` 均为 `189a5d7634aa69db54330c90f8711b05d8e44b10`。这是文档新增前的基线;最新文档节点以 `git log -1` 和远端引用为准。
|
||||||
|
|
||||||
## 先读和先查
|
## 先读和先查
|
||||||
|
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
| 入口 | 用途 |
|
| 入口 | 用途 |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
|
| [1.3.1 交付回执](verification/v1.3.1/delivery-receipt.json) | 当前安装版、签名源码、公证、清理及原生视觉待测范围 |
|
||||||
| [README](../README.md) | 产品介绍、使用与版本变化 |
|
| [README](../README.md) | 产品介绍、使用与版本变化 |
|
||||||
| [恢复说明](../RECOVERY.md) | 精确源码节点、依赖、远端恢复记录 |
|
| [恢复说明](../RECOVERY.md) | 精确源码节点、依赖、远端恢复记录 |
|
||||||
| [GitHub 同步说明](GitHub同步说明.md) | 两个远端、公开克隆及 Release 验证 |
|
| [GitHub 同步说明](GitHub同步说明.md) | 两个远端、公开克隆及 Release 验证 |
|
||||||
@@ -30,7 +31,7 @@
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
| 显示名称 | 大鱼人像美化 |
|
| 显示名称 | 大鱼人像美化 |
|
||||||
| Xcode 产品名 | `beatygril`,保留现有拼写 |
|
| Xcode 产品名 | `beatygril`,保留现有拼写 |
|
||||||
| APP 版本 | `1.2.0` / `14`,arm64 与 x86_64 通用版本 |
|
| APP 版本 | `1.3.1` / `16`,arm64 与 x86_64 通用版本 |
|
||||||
| 唯一正式 APP | `/Library/Plug-Ins/FxPlug/beatygril.app` |
|
| 唯一正式 APP | `/Library/Plug-Ins/FxPlug/beatygril.app` |
|
||||||
| 主 Bundle ID | `com.dayu.beatygril` |
|
| 主 Bundle ID | `com.dayu.beatygril` |
|
||||||
| 嵌套插件 | `Contents/PlugIns/beatygril XPC Service.pluginkit` |
|
| 嵌套插件 | `Contents/PlugIns/beatygril XPC Service.pluginkit` |
|
||||||
@@ -83,18 +84,19 @@ Xcode 工程:`beatygril/beatygril.xcodeproj`。外层 scheme 为 `Wrapper Appl
|
|||||||
|
|
||||||
本项目几何历史只取当前请求之前的近时记录,原版还存在近未来历史分支;冷启动、热缓存、乱序渲染、同尺寸换源和多人交叉仍有差异边界。旧离线分析字段保留兼容,但当前新面板的渲染统一使用实时自动跟随。
|
本项目几何历史只取当前请求之前的近时记录,原版还存在近未来历史分支;冷启动、热缓存、乱序渲染、同尺寸换源和多人交叉仍有差异边界。旧离线分析字段保留兼容,但当前新面板的渲染统一使用实时自动跟随。
|
||||||
|
|
||||||
### 已完成的四项局部算法
|
### 已完成的五项局部算法
|
||||||
|
|
||||||
| 控制项 | 当前实现 | 防止回归的要点 |
|
| 控制项 | 当前实现 | 防止回归的要点 |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| 瘦脸 | 76 点、17 组轮廓到鼻部或瞳孔锚点的局部逆向采样 | 不恢复整脸框水平挤压;保留尺寸增益、组顺序和逐步 clamp |
|
| 瘦脸 | 76 点、17 组轮廓到鼻部或瞳孔锚点的局部逆向采样 | 不恢复整脸框水平挤压;保留尺寸增益、组顺序和逐步 clamp |
|
||||||
|
| V 脸 | `69→39`、`65→35`、`70→26`、`64→34` 四组下颌到外唇局部位移 | 新版 2.4.0 使用 0.065 系数、无旧尺寸增益;两瞳孔保持不动 |
|
||||||
| 下巴 | `68→38`、`67→37`、`66→36` 三组局部位移 | 正值上收、负值延伸,保留正负不同系数 |
|
| 下巴 | `68→38`、`67→37`、`66→36` 三组局部位移 | 正值上收、负值延伸,保留正负不同系数 |
|
||||||
| 下颌骨 | 六组相邻轮廓半径的顺序局部位移 | 半径来自相邻轮廓点,方向锚点为 P29;不能同时求和替代逐步更新 |
|
| 下颌骨 | 六组相邻轮廓半径的顺序局部位移 | 半径来自相邻轮廓点,方向锚点为 P29;不能同时求和替代逐步更新 |
|
||||||
| 短脸 | P62 至 P72 的下半脸闭合区域,沿 P46−P48 鼻梁轴位移 | 正值收短、负值延长;不是太阳穴区域或脸框纵向压缩 |
|
| 短脸 | P62 至 P72 的下半脸闭合区域,沿 P46−P48 鼻梁轴位移 | 正值收短、负值延长;不是太阳穴区域或脸框纵向压缩 |
|
||||||
|
|
||||||
这四项共享由实际图像像素尺寸计算的增益。完整合法 76 点缺失时,四项安全直通,不退回猜测的脸框场。图像与磨皮、肤色、牙齿、眼睛蒙版共用几何变形路径,避免效果与蒙版错位。
|
此前四项沿用由实际图像像素尺寸计算的增益;V 脸对齐新版目标,不带该额外增益。完整合法 76 点缺失时,五项安全直通,不退回猜测的脸框场。图像与磨皮、肤色、牙齿、眼睛蒙版共用几何变形路径,避免效果与蒙版错位。
|
||||||
|
|
||||||
**仍为近似实现的五项:大眼、窄脸、V 脸、颧骨、小头。** 原版完整链为 `thin → narrow → V → jaw → cheek → chin → short → small-head → eyes`,本项目尚未完全恢复整条组合链的数学及顺序。用户单项验收不代表所有参数组合逐像素等价。详细索引、系数和独立数值场验证见[下巴与坐标分析](analysis/下巴与坐标分析.md)、[短脸与下颌骨分析](analysis/短脸与下颌骨分析.md)。
|
**仍为近似实现的四项:大眼、窄脸、颧骨、小头。** 原版完整链为 `thin → narrow → V → jaw → cheek → chin → short → small-head → eyes`,本项目尚未完全恢复整条组合链的数学及顺序。用户单项验收不代表所有参数组合逐像素等价。V 脸修复见 [下颌区域分析](analysis/V脸下颌区域修复.md)。详细索引、系数和独立数值场验证见[下巴与坐标分析](analysis/下巴与坐标分析.md)、[短脸与下颌骨分析](analysis/短脸与下颌骨分析.md)。
|
||||||
|
|
||||||
### 坐标约定
|
### 坐标约定
|
||||||
|
|
||||||
@@ -215,8 +217,8 @@ GitHub Release 下载名为 `Dayu-Portrait-1.2.0.pkg`,字节相同,已匿名
|
|||||||
|
|
||||||
文章及发布入口:
|
文章及发布入口:
|
||||||
|
|
||||||
- [公开源码仓库](https://github.com/FAMEDaYu/FCP-BeatyGril)
|
- [公开源码仓库](https://github.com/dayumeida/FCP-BeautyGirl)
|
||||||
- [1.2.0 GitHub Release](https://github.com/FAMEDaYu/FCP-BeatyGril/releases/tag/v1.2.0)
|
- [1.2.0 GitHub Release](https://github.com/dayumeida/FCP-BeautyGirl/releases/tag/v1.2.0)
|
||||||
- [大鱼站文章 15593](https://dayu.media/15593/)
|
- [大鱼站文章 15593](https://dayu.media/15593/)
|
||||||
- [大鱼 Git 备份](https://dayu.media/git/dayu/paintfcp-fxplug)
|
- [大鱼 Git 备份](https://dayu.media/git/dayu/paintfcp-fxplug)
|
||||||
|
|
||||||
@@ -231,12 +233,13 @@ GitHub Release 下载名为 `Dayu-Portrait-1.2.0.pkg`,字节相同,已匿名
|
|||||||
| 本地远端 | 地址 | 说明 |
|
| 本地远端 | 地址 | 说明 |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `origin` | `https://dayu.media/git/dayu/paintfcp-fxplug.git` | 保持为原远端及 main 上游;网站 Git 网关仍要求登录和有效会员 |
|
| `origin` | `https://dayu.media/git/dayu/paintfcp-fxplug.git` | 保持为原远端及 main 上游;网站 Git 网关仍要求登录和有效会员 |
|
||||||
| `github` | `https://github.com/FAMEDaYu/FCP-BeatyGril.git` | 公开仓库,匿名克隆可用,完整历史和七个标签已保存 |
|
| `github` | `https://github.com/dayumeida/FCP-BeautyGirl.git` | 公开仓库,匿名克隆可用,完整历史和七个标签已保存 |
|
||||||
|
|
||||||
本项目没有 LFS 或子模块。不要改写原远端、强推、覆盖现有标签或把双端同步描述成自动定时同步。
|
本项目没有 LFS 或子模块。不要改写原远端、强推、覆盖现有标签或把双端同步描述成自动定时同步。
|
||||||
|
|
||||||
| 节点 | 完整提交 SHA |
|
| 节点 | 完整提交 SHA |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
|
| 1.3.0 正式签名源码(codex/new-beauty-controls) | `2f2c081980dc610ac810f8daf92da8966f310802` |
|
||||||
| 1.2.0 正式签名源码 | `696d5f39b30404ed5d1d4c25f61d17475c7fc91b` |
|
| 1.2.0 正式签名源码 | `696d5f39b30404ed5d1d4c25f61d17475c7fc91b` |
|
||||||
| `v1.2.0` 发布标签 | `3c7261c3a861b9f4704cfb36055d7b500c239dab` |
|
| `v1.2.0` 发布标签 | `3c7261c3a861b9f4704cfb36055d7b500c239dab` |
|
||||||
| 1.1.2 已验收渲染及面板源码 | `a2e33e8e00a99b95dc29d67a819cb0897eda5756` |
|
| 1.1.2 已验收渲染及面板源码 | `a2e33e8e00a99b95dc29d67a819cb0897eda5756` |
|
||||||
@@ -247,8 +250,8 @@ GitHub Release 下载名为 `Dayu-Portrait-1.2.0.pkg`,字节相同,已匿名
|
|||||||
恢复正式发行源码可以:
|
恢复正式发行源码可以:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone https://github.com/FAMEDaYu/FCP-BeatyGril.git
|
git clone https://github.com/dayumeida/FCP-BeautyGirl.git
|
||||||
cd FCP-BeatyGril
|
cd FCP-BeautyGirl
|
||||||
git switch --detach v1.2.0
|
git switch --detach v1.2.0
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -258,7 +261,7 @@ git switch --detach v1.2.0
|
|||||||
|
|
||||||
提交和推送使用 `/Users/dayu/.codex/git-backup-locks/` 中的共享互斥锁。锁名为无凭据、转小写并去尾斜线的完整 HTTPS 仓库地址(保留 `.git`)的 SHA-256 加 `.lock`,`owner.json` 记录 PID、cwd、remote;不清理其他仍在工作的持有者。
|
提交和推送使用 `/Users/dayu/.codex/git-backup-locks/` 中的共享互斥锁。锁名为无凭据、转小写并去尾斜线的完整 HTTPS 仓库地址(保留 `.git`)的 SHA-256 加 `.lock`,`owner.json` 记录 PID、cwd、remote;不清理其他仍在工作的持有者。
|
||||||
|
|
||||||
本机 GitHub CLI 为 `/opt/homebrew/bin/gh`,本次已由用户登录 `FAMEDaYu`,HTTPS 凭据在钥匙串中。接手时用 `gh auth status` 确认;连接器曾使用其他只读账号,不能用连接器权限推断 CLI 登录失效,也不打印令牌。大鱼仓库管理使用 `dayu-git` CLI,指南在 `/Users/dayu/Documents/dayu-git/README.md` 与 `API.md`。
|
本机 GitHub CLI 为 `/opt/homebrew/bin/gh`,账号已更名为 `dayumeida`(账号ID117798680;CLI钥匙串条目可能仍以旧名标识,以 `gh api user --jq .login` 核对当前身份),HTTPS 凭据在钥匙串中。接手时用 `gh auth status` 确认;连接器曾使用其他只读账号,不能用连接器权限推断 CLI 登录失效,也不打印令牌。大鱼仓库管理使用 `dayu-git` CLI,指南在 `/Users/dayu/Documents/dayu-git/README.md` 与 `API.md`。
|
||||||
|
|
||||||
恢复范围仅覆盖已经提交、推送并核验的节点与实际上传的必要资源。用户素材、忽略的 build、私钥不在此范围。大鱼服务器当前只有手动备份,不能据此承诺本机与服务器同时损坏也可恢复。
|
恢复范围仅覆盖已经提交、推送并核验的节点与实际上传的必要资源。用户素材、忽略的 build、私钥不在此范围。大鱼服务器当前只有手动备份,不能据此承诺本机与服务器同时损坏也可恢复。
|
||||||
|
|
||||||
@@ -273,4 +276,13 @@ git switch --detach v1.2.0
|
|||||||
|
|
||||||
目标参考为 `/Applications/MSE18V2.app` 及用户原 MS 模板,仅用于只读分析。现有 REA、CPU 和 AIR 证据在 `docs/analysis/`;新分析按 `reverse-engineer-anything` 技能继续。不上传或打包原版 APP、模型、metallib、用户视频和完整人脸坐标。
|
目标参考为 `/Applications/MSE18V2.app` 及用户原 MS 模板,仅用于只读分析。现有 REA、CPU 和 AIR 证据在 `docs/analysis/`;新分析按 `reverse-engineer-anything` 技能继续。不上传或打包原版 APP、模型、metallib、用户视频和完整人脸坐标。
|
||||||
|
|
||||||
2026-10-22 新增瘦身迁移调查:用户指定 `/Users/dayu/Downloads/MSE18V2.app`(2.4.0 / 20260724150000),该包的参数注册及已检查静态范围尚未定位到“瘦身”板块。产品源码和正式安装未改,需先确认具体控制或正确目标;见[瘦身迁移目标核对](analysis/瘦身迁移目标核对.md)。不要把此调查写成已完成迁移。
|
2026-10-22 用户已纠正“瘦身”为新增面板参数,目标仍是 `/Users/dayu/Downloads/MSE18V2.app`(2.4.0 / 20260724150000)。现沿 `codex/new-beauty-controls` 接入连续五官保护、非肤色区保护和局部微细锐化,保留旧 30/17/4 参数及归档;见[新版磨皮参数迁移](analysis/新版磨皮参数迁移.md)。1.3.0(15)已正式安装,签名、票据、Gatekeeper、PluginKit 正式路径和已知临时副本清理均验证通过。原生视觉由用户测试,见新交付回执。[原瘦身目标核对](analysis/瘦身迁移目标核对.md)只保留历史,不再是待澄清项。
|
||||||
|
|
||||||
|
|
||||||
|
## 1.3.0 接续说明
|
||||||
|
|
||||||
|
用户已保存并退出 Motion/FCP,交付脚本在宿主全部退出后安装新 APP。Apple 公证 ID 为 `ca96837f-bf33-44fa-8d58-799026ce5c7d`,状态 Accepted;最终签名源码为 `2f2c081980dc610ac810f8daf92da8966f310802`。交付标签 `v1.3.0` 包含后续回执和恢复文档,源码分支为 `codex/new-beauty-controls`,main 仍保留原发行状态。
|
||||||
|
|
||||||
|
新增稳定 float ID:75 五官保护量、76 非肤色区保护、77 局部微细锐化。旧 bool 30、独立全局磨皮 17 和全局锐化 4 均保留;后两项在更多设置中,不能重新解释其历史数值。PGBeautySettings v2 只追加三个 float,接受明确的 v1/160 字节旧状态。PGInspectorState 仍为 schema 1,缺 75 时从旧 bool 导入;旧 bool 为 true 时不得覆盖已读到的新浮点值。
|
||||||
|
|
||||||
|
1636 项检查与离屏面板已验,含 ASan 宿主/OSC/Inspector;本次未在原生宿主中重新操作新滑杆或验收效果视觉。正式插件发现只核验 PluginKit 路径,不把它写作 FCP 或 Motion 实际加载/画面通过。当前无需重新安装、重做封面或更新 1.2.0 安装包;用户反馈实际效果后继续针对性处理。
|
||||||
+17
@@ -131,3 +131,20 @@ Motion:独立普通视频项目,源PTS0、1920×1080/25fps/10秒,媒体通
|
|||||||
## 1.1.2 连续预览用户验收补充(2026-10-22)
|
## 1.1.2 连续预览用户验收补充(2026-10-22)
|
||||||
|
|
||||||
用户明确回复“拖动中已持续更新”,确认最终1.1.2(13)按住滑杆拖动时画面能够持续更新,关闭上节待反馈项。此项证据来自用户原生实测;未指定具体宿主,不声称两个宿主分别完成按住拖动验收,也没有测量每个鼠标事件的独立排帧。本次仅更新验收记录,不改变产品源码、安装版或既有发布标签。
|
用户明确回复“拖动中已持续更新”,确认最终1.1.2(13)按住滑杆拖动时画面能够持续更新,关闭上节待反馈项。此项证据来自用户原生实测;未指定具体宿主,不声称两个宿主分别完成按住拖动验收,也没有测量每个鼠标事件的独立排帧。本次仅更新验收记录,不改变产品源码、安装版或既有发布标签。
|
||||||
|
|
||||||
|
|
||||||
|
## 1.3.0(15)新增磨皮参数与正式安装
|
||||||
|
|
||||||
|
按用户纠正后的截图需求,迁移连续五官保护、非肤色区保护和局部微细锐化;保留旧 bool、全局磨皮/锐化 ID 与旧工程行为。正式签名源码 `2f2c081980dc610ac810f8daf92da8966f310802`。1636 项通过:57 Beauty、116 Face、796 几何、341 宿主适配 ASan、98 OSC ASan、228 Inspector ASan(含 1 项可选 PNG 输出)。保护 0/50/100 像素响应、色彩覆盖隔离、透明度、v1 状态迁移、新控件归档/插值/输入/重置/旁路/松手前发布均有检查;离屏面板已查看。
|
||||||
|
|
||||||
|
通用 Release 正式签名、公证 Accepted(`ca96837f-bf33-44fa-8d58-799026ce5c7d`)、票据、双架构严格签名、安装身份、Gatekeeper、PluginKit 正式路径与临时副本清理通过。独立从 origin 克隆签名源码节点,205 个受跟踪文件逐字节匹配且 strict/full fsck 通过。本次没有原生新滑杆/效果视觉验收,也没有生成新 PKG 或更新文章;用户已保存并退出宿主后才执行安装。详见 `verification/v1.3.0/` 与 `analysis/新版磨皮参数迁移.md`。
|
||||||
|
|
||||||
|
|
||||||
|
## 1.3.1(16)V 脸下颌区域修复与正式交付
|
||||||
|
|
||||||
|
移除 V 脸的脸框近似宽带,改为新版目标的四组下颌到外唇 pin,单项映射为 clamp(V,0,1)×0.065,不叠加旧尺寸增益。无合法76点时直通,图像与四张蒙版同场变形。1901项自动检查通过:57 Beauty、116 Face、1033几何、370 Host ASan、98 OSC ASan、227 Inspector ASan。原失败复现、点位/CPU/AIR证据和未对齐范围见 analysis/V脸下颌区域修复.md。另对 NASA 人像和本机用户视频11帧运行1561项几何ASan专项(含重复基础用例,不相加),通过;用户帧和完整点坐标未上传。正式APP已安装,严格签名/双架构、票据、Gatekeeper、PluginKit正式路径及有界副本扫描通过,用户视觉仍待实测。APP公证Accepted e353b701-2fc2-4fcc-b03e-69af7735400f。
|
||||||
|
|
||||||
|
PKG由本机PKG工坊CLI从正式APP与用户指定已安装的「大鱼磨皮瘦脸」分类发布副本生成,含1个moef、Drop Zone.tiff和两张锁定缩略图。Installer签名、公证Accepted 78105340-306f-4629-8670-772760d4cb9a、票据、Gatekeeper、包内APP34个文件/链接与正式版一致、4模板资源、隔离新装/更新锁图/失败回滚均通过。本次未运行PKG安装器覆盖模板;正式APP通过交付脚本安装,模板保持原用户副本。PKG 5,011,286 bytes,SHA256 a6bb7262c9066e7abca8d1f21eaeaf1ba6a1d0d51b4859d39307aa7d7b0bc927。
|
||||||
|
|
||||||
|
|
||||||
|
1.3.1已发布至GitHub和大鱼Release,origin/github的main、修复分支与v1.3.1标签已同步回读。GitHub两个附件匿名下载哈希、安装包签名及票据通过;匿名独立标签克隆232文件、fsck和恢复包验证通过。文章15593就地更新为1.3.1,主下载PKG与源码ZIP的线上字节哈希一致,作者/封面/分类/免费及会员规则保留,前台正常和新请求的版本/说明/SEO通过。未登录前台仍显示免费及登录获取入口,未进行真实测试订单。大鱼Release只完成附件上传元数据回读,API令牌访问浏览器下载入口被重定向登录,未宣称该附件字节回读通过;必要PKG本体已随Git提交并在独立克隆中恢复。
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
# 大鱼人像美化 1.2.0(14)
|
# 大鱼人像美化 1.2.0(14)
|
||||||
|
|
||||||
- 发布文章:https://dayu.media/15593/
|
- 发布文章:https://dayu.media/15593/
|
||||||
- 公开源码:https://github.com/FAMEDaYu/FCP-BeatyGril
|
- 公开源码:https://github.com/dayumeida/FCP-BeautyGirl
|
||||||
- 大鱼备份:https://dayu.media/git/dayu/paintfcp-fxplug
|
- 大鱼备份:https://dayu.media/git/dayu/paintfcp-fxplug
|
||||||
- GitHub Release:https://github.com/FAMEDaYu/FCP-BeatyGril/releases/tag/v1.2.0
|
- GitHub Release:https://github.com/dayumeida/FCP-BeautyGirl/releases/tag/v1.2.0
|
||||||
- 大鱼 Release:https://dayu.media/git/dayu/paintfcp-fxplug/releases/tag/v1.2.0
|
- 大鱼 Release:https://dayu.media/git/dayu/paintfcp-fxplug/releases/tag/v1.2.0
|
||||||
- 标签完整 SHA:3c7261c3a861b9f4704cfb36055d7b500c239dab
|
- 标签完整 SHA:3c7261c3a861b9f4704cfb36055d7b500c239dab
|
||||||
- 正式 APP 源码:696d5f39b30404ed5d1d4c25f61d17475c7fc91b
|
- 正式 APP 源码:696d5f39b30404ed5d1d4c25f61d17475c7fc91b
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"title": "大鱼人像美化 1.2.0:FCP / Motion 免费开源自动美颜插件",
|
"title": "大鱼人像美化 1.2.0:FCP / Motion 免费开源自动美颜插件",
|
||||||
"slug": "dayu-portrait-beauty",
|
"slug": "dayu-portrait-beauty",
|
||||||
"status": "publish",
|
"status": "publish",
|
||||||
"content": "<!-- wp:paragraph -->\n<p>大鱼人像美化是一款适用于 Final Cut Pro 与 Motion 的免费开源人像插件,把磨皮、美白和脸型调整放进同一块原生卡片面板。插件会识别当前画面中的人脸并自动跟随,无需先点击分析;你可以按素材需要调整强度,拖动滑杆时持续查看效果。</p>\n<!-- /wp:paragraph -->\n<!-- wp:paragraph -->\n<p><strong>代码由 OpenAI + Codex 全程编写,站长仅参与调试和测试。</strong></p>\n<!-- /wp:paragraph -->\n<!-- wp:image {\"sizeSlug\":\"full\"} -->\n<figure class=\"wp-block-image size-full\"><img src=\"{{media.cover.url}}\" alt=\"大鱼人像美化古风宣传封面,展示自动瘦脸、自动跟随与自动美颜\"/><figcaption>古风宣传插画;面部线条用于示意瘦脸与跟随功能。</figcaption></figure>\n<!-- /wp:image -->\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">可以做什么</h2>\n<!-- /wp:heading -->\n<!-- wp:list -->\n<ul class=\"wp-block-list\"><li>磨皮:局部磨皮、全局磨皮与五官保护,强度分别控制。</li><li>美白:肤色预设、肤色亮度、皮肤色相和牙齿美白。</li><li>脸型:瘦脸、大眼、短脸、窄脸、下巴、V 脸、下颌骨、颧骨与小头调整。</li><li>自动跟随:默认跟随主要人脸,也提供多人模式、防跳脸范围与检测框预览。</li><li>实时预览:滑杆、数值、单项开关和重置集中在同一面板,便于反复对比。</li></ul>\n<!-- /wp:list -->\n<!-- wp:paragraph -->\n<p>自动识别负责定位处理区域,效果强度由你控制。建议先从较小的数值开始;多人交叉、严重遮挡和极端侧脸的结果需要结合具体素材检查。</p>\n<!-- /wp:paragraph -->\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">版本与下载</h2>\n<!-- /wp:heading -->\n<!-- wp:paragraph -->\n<p>当前版本 <strong>1.2.0(14)</strong>,支持 macOS 13 或更高版本,包含 Apple Silicon 与 Intel 两种架构。下载包包含 FxPlug4 程序和一个配套 FCP 效果模板。APP 与 PKG 均已完成 Developer ID 正式签名、Apple 公证和票据粘附;安装包为所有用户免费资源。</p>\n<!-- /wp:paragraph -->\n<!-- wp:paragraph -->\n<p>本次更新加入古风封面与锁定缩略图、免费开源说明页和主 APP 三秒自动退出。人脸处理与面板交互沿用 1.1.2 已验收实现,不需要重新运行手动分析。</p>\n<!-- /wp:paragraph -->\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">安装</h2>\n<!-- /wp:heading -->\n<!-- wp:paragraph -->\n<p>安装前先保存工作并退出 Final Cut Pro 和 Motion,安装期间保持关闭。运行本页提供的 PKG,按安装器提示完成安装;它会把程序安装到 <code>/Library/Plug-Ins/FxPlug/beatygril.app</code>,把配套效果安装到当前登录用户的 Motion Templates 目录,并自动验证和修复系统插件注册。</p>\n<!-- /wp:paragraph -->\n<!-- wp:paragraph -->\n<p>本插件免费开源,<strong>无需激活码、付费注册或账号授权</strong>。主 APP 打开时显示说明图,窗口显示三秒后自动退出;宿主中的滤镜由独立服务运行。安装器还会重新锁定两张缩略图,避免被宿主自动替换。</p>\n<!-- /wp:paragraph -->\n<!-- wp:image {\"sizeSlug\":\"full\"} -->\n<figure class=\"wp-block-image size-full\"><img src=\"https://dayu.media/wp-content/uploads/2026/10/1792604743-1c99c2ba53c7614.png\" alt=\"主程序说明页:免费开源,自动注册,无需激活码,启动三秒后自动退出\"/></figure>\n<!-- /wp:image -->\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">使用</h2>\n<!-- /wp:heading -->\n<!-- wp:paragraph -->\n<p>Final Cut Pro:打开<strong>效果</strong>浏览器,找到<strong>大鱼磨皮瘦脸</strong>分类,将<strong>大鱼人像美化</strong>添加到人像片段。Motion:在原生滤镜资源库的<strong>大鱼</strong>分类中选择<strong>大鱼人像美化</strong>。</p>\n<!-- /wp:paragraph -->\n<!-- wp:paragraph -->\n<p>在检查器中按需调节磨皮、美白和脸型。每项左侧的眼睛可暂时停用该项,数值会保留;右侧回转箭头用于重置。人脸会随画面自动更新,开启检测框预览可以检查跟随位置。多人模式建议先用短片段观察稳定性。</p>\n<!-- /wp:paragraph -->\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">常见问题</h2>\n<!-- /wp:heading -->\n<!-- wp:paragraph -->\n<p>如果效果没有出现或显示红色警告,先退出 Final Cut Pro 和 Motion,再打开一次主 APP,等待其自动退出,然后重新启动宿主。可在“终端”执行下面这条命令:</p>\n<!-- /wp:paragraph -->\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>open '/Library/Plug-Ins/FxPlug/beatygril.app'</code></pre>\n<!-- /wp:code -->\n<!-- wp:paragraph -->\n<p>该流程可重新触发系统注册,并不能解决所有报错。仍异常时请核对 macOS、Line truncated
|
"content": "<!-- wp:paragraph -->\n<p>大鱼人像美化是一款适用于 Final Cut Pro 与 Motion 的免费开源人像插件,把磨皮、美白和脸型调整放进同一块原生卡片面板。插件会识别当前画面中的人脸并自动跟随,无需先点击分析;你可以按素材需要调整强度,拖动滑杆时持续查看效果。</p>\n<!-- /wp:paragraph -->\n<!-- wp:paragraph -->\n<p><strong>代码由 OpenAI + Codex 全程编写,站长仅参与调试和测试。</strong></p>\n<!-- /wp:paragraph -->\n<!-- wp:image {\"sizeSlug\":\"full\"} -->\n<figure class=\"wp-block-image size-full\"><img src=\"{{media.cover.url}}\" alt=\"大鱼人像美化古风宣传封面,展示自动瘦脸、自动跟随与自动美颜\"/><figcaption>古风宣传插画;面部线条用于示意瘦脸与跟随功能。</figcaption></figure>\n<!-- /wp:image -->\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">可以做什么</h2>\n<!-- /wp:heading -->\n<!-- wp:list -->\n<ul class=\"wp-block-list\"><li>磨皮:局部磨皮、全局磨皮与五官保护,强度分别控制。</li><li>美白:肤色预设、肤色亮度、皮肤色相和牙齿美白。</li><li>脸型:瘦脸、大眼、短脸、窄脸、下巴、V 脸、下颌骨、颧骨与小头调整。</li><li>自动跟随:默认跟随主要人脸,也提供多人模式、防跳脸范围与检测框预览。</li><li>实时预览:滑杆、数值、单项开关和重置集中在同一面板,便于反复对比。</li></ul>\n<!-- /wp:list -->\n<!-- wp:paragraph -->\n<p>自动识别负责定位处理区域,效果强度由你控制。建议先从较小的数值开始;多人交叉、严重遮挡和极端侧脸的结果需要结合具体素材检查。</p>\n<!-- /wp:paragraph -->\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">版本与下载</h2>\n<!-- /wp:heading -->\n<!-- wp:paragraph -->\n<p>当前版本 <strong>1.2.0(14)</strong>,支持 macOS 13 或更高版本,包含 Apple Silicon 与 Intel 两种架构。下载包包含 FxPlug4 程序和一个配套 FCP 效果模板。APP 与 PKG 均已完成 Developer ID 正式签名、Apple 公证和票据粘附;安装包为所有用户免费资源。</p>\n<!-- /wp:paragraph -->\n<!-- wp:paragraph -->\n<p>本次更新加入古风封面与锁定缩略图、免费开源说明页和主 APP 三秒自动退出。人脸处理与面板交互沿用 1.1.2 已验收实现,不需要重新运行手动分析。</p>\n<!-- /wp:paragraph -->\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">安装</h2>\n<!-- /wp:heading -->\n<!-- wp:paragraph -->\n<p>安装前先保存工作并退出 Final Cut Pro 和 Motion,安装期间保持关闭。运行本页提供的 PKG,按安装器提示完成安装;它会把程序安装到 <code>/Library/Plug-Ins/FxPlug/beatygril.app</code>,把配套效果安装到当前登录用户的 Motion Templates 目录,并自动验证和修复系统插件注册。</p>\n<!-- /wp:paragraph -->\n<!-- wp:paragraph -->\n<p>本插件免费开源,<strong>无需激活码、付费注册或账号授权</strong>。主 APP 打开时显示说明图,窗口显示三秒后自动退出;宿主中的滤镜由独立服务运行。安装器还会重新锁定两张缩略图,避免被宿主自动替换。</p>\n<!-- /wp:paragraph -->\n<!-- wp:image {\"sizeSlug\":\"full\"} -->\n<figure class=\"wp-block-image size-full\"><img src=\"https://dayu.media/wp-content/uploads/2026/10/1792604743-1c99c2ba53c7614.png\" alt=\"主程序说明页:免费开源,自动注册,无需激活码,启动三秒后自动退出\"/></figure>\n<!-- /wp:image -->\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">使用</h2>\n<!-- /wp:heading -->\n<!-- wp:paragraph -->\n<p>Final Cut Pro:打开<strong>效果</strong>浏览器,找到<strong>大鱼磨皮瘦脸</strong>分类,将<strong>大鱼人像美化</strong>添加到人像片段。Motion:在原生滤镜资源库的<strong>大鱼</strong>分类中选择<strong>大鱼人像美化</strong>。</p>\n<!-- /wp:paragraph -->\n<!-- wp:paragraph -->\n<p>在检查器中按需调节磨皮、美白和脸型。每项左侧的眼睛可暂时停用该项,数值会保留;右侧回转箭头用于重置。人脸会随画面自动更新,开启检测框预览可以检查跟随位置。多人模式建议先用短片段观察稳定性。</p>\n<!-- /wp:paragraph -->\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">常见问题</h2>\n<!-- /wp:heading -->\n<!-- wp:paragraph -->\n<p>如果效果没有出现或显示红色警告,先退出 Final Cut Pro 和 Motion,再打开一次主 APP,等待其自动退出,然后重新启动宿主。可在“终端”执行下面这条命令:</p>\n<!-- /wp:paragraph -->\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>open '/Library/Plug-Ins/FxPlug/beatygril.app'</code></pre>\n<!-- /wp:code -->\n<!-- wp:paragraph -->\n<p>该流程可重新触发系统注册,并不能解决所有报错。仍异常时请核对 macOS、Line truncated
|
||||||
"excerpt": "磨皮、美白、瘦脸与自动人脸跟随,原生卡片面板实时预览。免费开源,自动注册,无需激活码;提供签名公证安装包和源码。",
|
"excerpt": "磨皮、美白、瘦脸与自动人脸跟随,原生卡片面板实时预览。免费开源,自动注册,无需激活码;提供签名公证安装包和源码。",
|
||||||
"categories": [
|
"categories": [
|
||||||
3
|
3
|
||||||
|
|||||||
@@ -62,7 +62,7 @@
|
|||||||
<h2 class="wp-block-heading">源码与开源许可</h2>
|
<h2 class="wp-block-heading">源码与开源许可</h2>
|
||||||
<!-- /wp:heading -->
|
<!-- /wp:heading -->
|
||||||
<!-- wp:paragraph -->
|
<!-- wp:paragraph -->
|
||||||
<p>公开源码仓库:<a href="https://github.com/FAMEDaYu/FCP-BeatyGril" target="_blank" rel="noreferrer noopener">GitHub · FCP-BeatyGril</a>,可直接浏览源码、完整提交历史和版本标签。<a href="https://github.com/FAMEDaYu/FCP-BeatyGril/releases/tag/v1.2.0" target="_blank" rel="noreferrer noopener">1.2.0 Release</a> 提供正式签名公证安装包。大鱼站仓库作为备份保留:<a href="https://dayu.media/git/dayu/paintfcp-fxplug" target="_blank" rel="noreferrer noopener">paintfcp-fxplug</a>(该入口仍受站点登录与会员网关限制)。也可下载 <a href="{{source_url}}">1.2.0 源码快照 ZIP</a>,源码与安装包均免费提供。</p>
|
<p>公开源码仓库:<a href="https://github.com/dayumeida/FCP-BeautyGirl" target="_blank" rel="noreferrer noopener">GitHub · FCP-BeautyGirl</a>,可直接浏览源码、完整提交历史和版本标签。<a href="https://github.com/dayumeida/FCP-BeautyGirl/releases/tag/v1.2.0" target="_blank" rel="noreferrer noopener">1.2.0 Release</a> 提供正式签名公证安装包。大鱼站仓库作为备份保留:<a href="https://dayu.media/git/dayu/paintfcp-fxplug" target="_blank" rel="noreferrer noopener">paintfcp-fxplug</a>(该入口仍受站点登录与会员网关限制)。也可下载 <a href="{{source_url}}">1.2.0 源码快照 ZIP</a>,源码与安装包均免费提供。</p>
|
||||||
<!-- /wp:paragraph -->
|
<!-- /wp:paragraph -->
|
||||||
<!-- wp:paragraph -->
|
<!-- wp:paragraph -->
|
||||||
<p>项目代码按仓库中的 MIT 许可文件提供,源码包含 Xcode 工程、Objective-C / Metal 实现、模板及构建与验证说明。Apple SDK、运行框架及样本按各自许可使用,详见源码中的 LICENSE 和 THIRD_PARTY_NOTICES.md。自行编译需要 Xcode 与 FxPlug SDK;证书和签名私钥不包含在源码中。</p>
|
<p>项目代码按仓库中的 MIT 许可文件提供,源码包含 Xcode 工程、Objective-C / Metal 实现、模板及构建与验证说明。Apple SDK、运行框架及样本按各自许可使用,详见源码中的 LICENSE 和 THIRD_PARTY_NOTICES.md。自行编译需要 Xcode 与 FxPlug SDK;证书和签名私钥不包含在源码中。</p>
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# 大鱼人像美化 1.3.1(16)
|
||||||
|
|
||||||
|
修复V脸作用区域,改为四组实际下颌轮廓到外唇的局部采样;同时包含1.3.0的微细锐化、连续五官保护与非肤色区保护。1901项自动回归通过,另有1561项重叠真实帧几何ASan检查;新效果在原生宿主中的最终视觉仍待用户结合素材实测。
|
||||||
|
|
||||||
|
- 文章:https://dayu.media/15593/
|
||||||
|
- GitHub:https://github.com/dayumeida/FCP-BeautyGirl
|
||||||
|
- 大鱼备份:https://dayu.media/git/dayu/paintfcp-fxplug
|
||||||
|
- 发布标签:v1.3.1
|
||||||
|
- 正式APP源码:7fa3298e022946e77dfd8cfba1bb5ff2c2a6361f
|
||||||
|
- APP公证:Accepted e353b701-2fc2-4fcc-b03e-69af7735400f
|
||||||
|
- PKG公证:Accepted 78105340-306f-4629-8670-772760d4cb9a
|
||||||
|
- PKG:大鱼人像美化-1.3.1.pkg,5,011,286 bytes
|
||||||
|
- SHA-256:a6bb7262c9066e7abca8d1f21eaeaf1ba6a1d0d51b4859d39307aa7d7b0bc927
|
||||||
|
|
||||||
|
由本机PKG工坊CLI制作,APP来自唯一正式路径,模板来自用户指定的已安装分类「大鱼磨皮瘦脸」。包含「大鱼人像美化.moef」、Drop Zone.tiff、large.png和small.png;两张缩略图的锁定由安装脚本恢复。PKG签名、公证票据、Gatekeeper、载荷逐字节及隔离脚本测试通过,未实际运行本PKG安装器。APP已通过正式交付流程安装到/Library/Plug-Ins/FxPlug/beatygril.app。
|
||||||
|
|
||||||
|
安装前退出FCP/Motion;FCP在效果→大鱼磨皮瘦脸→大鱼人像美化中使用。免费开源,无需激活码。代码由OpenAI + Codex全程编写,站长仅参与调试和测试。Apple SDK及相关资源遵循各自许可。
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
"title": "大鱼人像美化 1.3.1:FCP / Motion 免费开源自动美颜插件",
|
||||||
|
"status": "publish",
|
||||||
|
"content": "<!-- wp:paragraph -->\n<p>大鱼人像美化是一款适用于 Final Cut Pro 与 Motion 的免费开源人像插件,把磨皮、美白和脸型调整放进同一块原生卡片面板。插件会识别当前画面中的人脸并自动跟随,无需先点击分析;你可以按素材需要调整强度,拖动滑杆时持续查看效果。</p>\n<!-- /wp:paragraph -->\n<!-- wp:paragraph -->\n<p><strong>代码由 OpenAI + Codex 全程编写,站长仅参与调试和测试。</strong></p>\n<!-- /wp:paragraph -->\n\n<!-- wp:image {\"sizeSlug\":\"full\"} -->\n<figure class=\"wp-block-image size-full\"><img src=\"https://dayu.media/wp-content/uploads/2026/10/1792604750-357ca780597c39f.png\" alt=\"大鱼人像美化古风宣传封面,展示自动瘦脸、自动跟随与自动美颜\" /><figcaption class=\"wp-element-caption\">古风宣传插画;面部线条用于示意瘦脸与跟随功能。</figcaption></figure>\n<!-- /wp:image -->\n\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">可以做什么</h2>\n<!-- /wp:heading -->\n\n<!-- wp:list {\"className\":\"wp-block-list\"} -->\n<ul class=\"wp-block-list\"><!-- wp:list-item -->\n<li>磨皮:磨皮程度、微细锐化、五官保护和非肤色区保护分别调节;全局磨皮与锐化保留在更多设置。</li>\n<!-- /wp:list-item -->\n\n<!-- wp:list-item -->\n<li>美白:肤色预设、肤色亮度、皮肤色相和牙齿美白。</li>\n<!-- /wp:list-item -->\n\n<!-- wp:list-item -->\n<li>脸型:瘦脸、大眼、短脸、窄脸、下巴、V 脸、下颌骨、颧骨与小头调整。</li>\n<!-- /wp:list-item -->\n\n<!-- wp:list-item -->\n<li>自动跟随:默认跟随主要人脸,也提供多人模式、防跳脸范围与检测框预览。</li>\n<!-- /wp:list-item -->\n\n<!-- wp:list-item -->\n<li>实时预览:滑杆、数值、单项开关和重置集中在同一面板,便于反复对比。</li>\n<!-- /wp:list-item --></ul>\n<!-- /wp:list -->\n\n<!-- wp:paragraph -->\n<p>自动识别负责定位处理区域,效果强度由你控制。建议先从较小的数值开始;多人交叉、严重遮挡和极端侧脸的结果需要结合具体素材检查。</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">版本与下载</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>当前版本 <strong>1.3.1(16)</strong>,支持 macOS 13 或更高版本,包含 Apple Silicon 与 Intel 两种架构。下载包包含 FxPlug4 程序和一个配套 FCP 效果模板。APP 与 PKG 均已完成 Developer ID 正式签名、Apple 公证和票据粘附;安装包为所有用户免费资源。</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>1.3.1 修复 V 脸影响眼区的问题,改为沿实际下颌轮廓进行局部调整;同时包含 1.3.0 新增的微细锐化、连续五官保护和非肤色区保护。两项保护默认 100%,支持旧工程参数。1901 项自动检查通过,另有真实帧几何检查;新参数及 V 脸在原生宿主中的最终视觉效果仍需结合素材实测。</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">安装</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>安装前先保存工作并退出 Final Cut Pro 和 Motion,安装期间保持关闭。运行本页提供的 PKG,按安装器提示完成安装;它会把程序安装到 <code>/Library/Plug-Ins/FxPlug/beatygril.app</code>,把配套效果安装到当前登录用户的 Motion Templates 目录,并自动验证和修复系统插件注册。</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>本插件免费开源,<strong>无需激活码、付费注册或账号授权</strong>。主 APP 打开时显示说明图,窗口显示三秒后自动退出;宿主中的滤镜由独立服务运行。安装器还会重新锁定两张缩略图,避免被宿主自动替换。</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:image {\"sizeSlug\":\"full\"} -->\n<figure class=\"wp-block-image size-full\"><img src=\"https://dayu.media/wp-content/uploads/2026/10/1792604743-1c99c2ba53c7614.png\" alt=\"主程序说明页:免费开源,自动注册,无需激活码,启动三秒后自动退出\" /></figure>\n<!-- /wp:image -->\n\n<!-- wp:heading -->\n<h2 class=\"wp-block-heading\">使用</h2>\n<!-- /wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Final Cut Pro:打开<strong>效果</strong>浏览器,找到<strong>大鱼磨皮瘦脸</strong>分类,将<strong>大鱼人像美化</strong>添加到人像片段。Motion:在原生滤镜资源库的<strong>大鱼</strong>分类中选择<strong>大鱼人像美化</strong>。</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>在检查器中按需调节磨皮、美白和脸型。每项左侧的眼睛可暂时停用该项,数值会保留;右侧回转箭头用于重置。人脸会随画面自动更新,开启检测框预览可以检查跟随位置。多人模式建议先用短片段观察稳定性。</p>\n<!-- /wpLine truncated
|
||||||
|
"excerpt": "1.3.1 修复 V 脸下颌作用区域,新增微细锐化、五官保护与非肤色区保护。免费开源,提供签名公证安装包及源码。",
|
||||||
|
"resource": {
|
||||||
|
"cao_status": true,
|
||||||
|
"cao_price": 0,
|
||||||
|
"cao_vip_rate": 1,
|
||||||
|
"cao_info": [
|
||||||
|
{
|
||||||
|
"title": "版本",
|
||||||
|
"desc": "1.3.1(16)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "宿主",
|
||||||
|
"desc": "Final Cut Pro / Motion"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "系统",
|
||||||
|
"desc": "macOS 13 或更高版本"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "架构",
|
||||||
|
"desc": "Apple Silicon / Intel"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "授权",
|
||||||
|
"desc": "免费开源,无需激活码"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"seo": {
|
||||||
|
"title": {
|
||||||
|
"enabled": true,
|
||||||
|
"value": "大鱼人像美化 1.3.1|FCP / Motion 免费开源自动美颜插件"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"enabled": true,
|
||||||
|
"value": "大鱼人像美化是适用于 Final Cut Pro 和 Motion 的免费开源 FxPlug4 插件,1.3.1 修复 V 脸下颌作用区域,并加入微细锐化、五官保护与非肤色区保护。提供 Apple 签名公证 PKG 安装包和源码,安装自动注册,无需激活码,附安装使用及故障排查说明。"
|
||||||
|
},
|
||||||
|
"keywords": {
|
||||||
|
"enabled": true,
|
||||||
|
"value": "大鱼人像美化,FCP美颜插件,Motion磨皮,自动瘦脸,自动跟随,FxPlug4,免费开源"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
<!-- wp:paragraph -->
|
||||||
|
<p>大鱼人像美化是一款适用于 Final Cut Pro 与 Motion 的免费开源人像插件,把磨皮、美白和脸型调整放进同一块原生卡片面板。插件会识别当前画面中的人脸并自动跟随,无需先点击分析;你可以按素材需要调整强度,拖动滑杆时持续查看效果。</p>
|
||||||
|
<!-- /wp:paragraph -->
|
||||||
|
<!-- wp:paragraph -->
|
||||||
|
<p><strong>代码由 OpenAI + Codex 全程编写,站长仅参与调试和测试。</strong></p>
|
||||||
|
<!-- /wp:paragraph -->
|
||||||
|
|
||||||
|
<!-- wp:image {"sizeSlug":"full"} -->
|
||||||
|
<figure class="wp-block-image size-full"><img src="https://dayu.media/wp-content/uploads/2026/10/1792604750-357ca780597c39f.png" alt="大鱼人像美化古风宣传封面,展示自动瘦脸、自动跟随与自动美颜" /><figcaption class="wp-element-caption">古风宣传插画;面部线条用于示意瘦脸与跟随功能。</figcaption></figure>
|
||||||
|
<!-- /wp:image -->
|
||||||
|
|
||||||
|
<!-- wp:heading -->
|
||||||
|
<h2 class="wp-block-heading">可以做什么</h2>
|
||||||
|
<!-- /wp:heading -->
|
||||||
|
|
||||||
|
<!-- wp:list {"className":"wp-block-list"} -->
|
||||||
|
<ul class="wp-block-list"><!-- wp:list-item -->
|
||||||
|
<li>磨皮:磨皮程度、微细锐化、五官保护和非肤色区保护分别调节;全局磨皮与锐化保留在更多设置。</li>
|
||||||
|
<!-- /wp:list-item -->
|
||||||
|
|
||||||
|
<!-- wp:list-item -->
|
||||||
|
<li>美白:肤色预设、肤色亮度、皮肤色相和牙齿美白。</li>
|
||||||
|
<!-- /wp:list-item -->
|
||||||
|
|
||||||
|
<!-- wp:list-item -->
|
||||||
|
<li>脸型:瘦脸、大眼、短脸、窄脸、下巴、V 脸、下颌骨、颧骨与小头调整。</li>
|
||||||
|
<!-- /wp:list-item -->
|
||||||
|
|
||||||
|
<!-- wp:list-item -->
|
||||||
|
<li>自动跟随:默认跟随主要人脸,也提供多人模式、防跳脸范围与检测框预览。</li>
|
||||||
|
<!-- /wp:list-item -->
|
||||||
|
|
||||||
|
<!-- wp:list-item -->
|
||||||
|
<li>实时预览:滑杆、数值、单项开关和重置集中在同一面板,便于反复对比。</li>
|
||||||
|
<!-- /wp:list-item --></ul>
|
||||||
|
<!-- /wp:list -->
|
||||||
|
|
||||||
|
<!-- wp:paragraph -->
|
||||||
|
<p>自动识别负责定位处理区域,效果强度由你控制。建议先从较小的数值开始;多人交叉、严重遮挡和极端侧脸的结果需要结合具体素材检查。</p>
|
||||||
|
<!-- /wp:paragraph -->
|
||||||
|
|
||||||
|
<!-- wp:heading -->
|
||||||
|
<h2 class="wp-block-heading">版本与下载</h2>
|
||||||
|
<!-- /wp:heading -->
|
||||||
|
|
||||||
|
<!-- wp:paragraph -->
|
||||||
|
<p>当前版本 <strong>1.3.1(16)</strong>,支持 macOS 13 或更高版本,包含 Apple Silicon 与 Intel 两种架构。下载包包含 FxPlug4 程序和一个配套 FCP 效果模板。APP 与 PKG 均已完成 Developer ID 正式签名、Apple 公证和票据粘附;安装包为所有用户免费资源。</p>
|
||||||
|
<!-- /wp:paragraph -->
|
||||||
|
|
||||||
|
<!-- wp:paragraph -->
|
||||||
|
<p>1.3.1 修复 V 脸影响眼区的问题,改为沿实际下颌轮廓进行局部调整;同时包含 1.3.0 新增的微细锐化、连续五官保护和非肤色区保护。两项保护默认 100%,支持旧工程参数。1901 项自动检查通过,另有真实帧几何检查;新参数及 V 脸在原生宿主中的最终视觉效果仍需结合素材实测。</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="https://dayu.media/wp-content/uploads/2026/10/1792604743-1c99c2ba53c7614.png" 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://github.com/dayumeida/FCP-BeautyGirl" target="_blank" rel="noreferrer noopener">GitHub · FCP-BeautyGirl</a>,可直接浏览源码、完整提交历史和版本标签。<a href="https://github.com/dayumeida/FCP-BeautyGirl/releases/tag/v1.3.1" target="_blank" rel="noreferrer noopener">1.3.1 Release</a> 提供正式签名公证安装包。大鱼站仓库作为备份保留:<a href="https://dayu.media/git/dayu/paintfcp-fxplug" target="_blank" rel="noreferrer noopener">paintfcp-fxplug</a>(该入口仍受站点登录与会员网关限制)。也可下载 <a href="https://dayu.media/wp-content/uploads/2026/10/1792659515-d487b0a421aacc6.zip">1.3.1 源码快照 ZIP</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 -->
|
||||||
|
|
||||||
|
<!-- wp:image {"id":15597,"sizeSlug":"large","linkDestination":"none"} -->
|
||||||
|
<figure class="wp-block-image size-large"><img src="https://dayu.media/wp-content/uploads/2026/10/1792605142-78805a221a988e7-359x1024.png" alt="" class="wp-image-15597" /></figure>
|
||||||
|
<!-- /wp:image -->
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
<!-- wp:paragraph -->
|
||||||
|
<p>大鱼人像美化是一款适用于 Final Cut Pro 与 Motion 的免费开源人像插件,把磨皮、美白和脸型调整放进同一块原生卡片面板。插件会识别当前画面中的人脸并自动跟随,无需先点击分析;你可以按素材需要调整强度,拖动滑杆时持续查看效果。</p>
|
||||||
|
<!-- /wp:paragraph -->
|
||||||
|
<!-- wp:paragraph -->
|
||||||
|
<p><strong>代码由 OpenAI + Codex 全程编写,站长仅参与调试和测试。</strong></p>
|
||||||
|
<!-- /wp:paragraph -->
|
||||||
|
|
||||||
|
<!-- wp:image {"sizeSlug":"full"} -->
|
||||||
|
<figure class="wp-block-image size-full"><img src="https://dayu.media/wp-content/uploads/2026/10/1792604750-357ca780597c39f.png" alt="大鱼人像美化古风宣传封面,展示自动瘦脸、自动跟随与自动美颜" /><figcaption class="wp-element-caption">古风宣传插画;面部线条用于示意瘦脸与跟随功能。</figcaption></figure>
|
||||||
|
<!-- /wp:image -->
|
||||||
|
|
||||||
|
<!-- wp:heading -->
|
||||||
|
<h2 class="wp-block-heading">可以做什么</h2>
|
||||||
|
<!-- /wp:heading -->
|
||||||
|
|
||||||
|
<!-- wp:list {"className":"wp-block-list"} -->
|
||||||
|
<ul class="wp-block-list"><!-- wp:list-item -->
|
||||||
|
<li>磨皮:磨皮程度、微细锐化、五官保护和非肤色区保护分别调节;全局磨皮与锐化保留在更多设置。</li>
|
||||||
|
<!-- /wp:list-item -->
|
||||||
|
|
||||||
|
<!-- wp:list-item -->
|
||||||
|
<li>美白:肤色预设、肤色亮度、皮肤色相和牙齿美白。</li>
|
||||||
|
<!-- /wp:list-item -->
|
||||||
|
|
||||||
|
<!-- wp:list-item -->
|
||||||
|
<li>脸型:瘦脸、大眼、短脸、窄脸、下巴、V 脸、下颌骨、颧骨与小头调整。</li>
|
||||||
|
<!-- /wp:list-item -->
|
||||||
|
|
||||||
|
<!-- wp:list-item -->
|
||||||
|
<li>自动跟随:默认跟随主要人脸,也提供多人模式、防跳脸范围与检测框预览。</li>
|
||||||
|
<!-- /wp:list-item -->
|
||||||
|
|
||||||
|
<!-- wp:list-item -->
|
||||||
|
<li>实时预览:滑杆、数值、单项开关和重置集中在同一面板,便于反复对比。</li>
|
||||||
|
<!-- /wp:list-item --></ul>
|
||||||
|
<!-- /wp:list -->
|
||||||
|
|
||||||
|
<!-- wp:paragraph -->
|
||||||
|
<p>自动识别负责定位处理区域,效果强度由你控制。建议先从较小的数值开始;多人交叉、严重遮挡和极端侧脸的结果需要结合具体素材检查。</p>
|
||||||
|
<!-- /wp:paragraph -->
|
||||||
|
|
||||||
|
<!-- wp:heading -->
|
||||||
|
<h2 class="wp-block-heading">版本与下载</h2>
|
||||||
|
<!-- /wp:heading -->
|
||||||
|
|
||||||
|
<!-- wp:paragraph -->
|
||||||
|
<p>当前版本 <strong>1.3.1(16)</strong>,支持 macOS 13 或更高版本,包含 Apple Silicon 与 Intel 两种架构。下载包包含 FxPlug4 程序和一个配套 FCP 效果模板。APP 与 PKG 均已完成 Developer ID 正式签名、Apple 公证和票据粘附;安装包为所有用户免费资源。</p>
|
||||||
|
<!-- /wp:paragraph -->
|
||||||
|
|
||||||
|
<!-- wp:paragraph -->
|
||||||
|
<p>1.3.1 修复 V 脸影响眼区的问题,改为沿实际下颌轮廓进行局部调整;同时包含 1.3.0 新增的微细锐化、连续五官保护和非肤色区保护。两项保护默认 100%,支持旧工程参数。1901 项自动检查通过,另有真实帧几何检查;新参数及 V 脸在原生宿主中的最终视觉效果仍需结合素材实测。</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="https://dayu.media/wp-content/uploads/2026/10/1792604743-1c99c2ba53c7614.png" 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://github.com/dayumeida/FCP-BeautyGirl" target="_blank" rel="noreferrer noopener">GitHub · FCP-BeautyGirl</a>,可直接浏览源码、完整提交历史和版本标签。<a href="https://github.com/dayumeida/FCP-BeautyGirl/releases/tag/v1.3.1" target="_blank" rel="noreferrer noopener">1.3.1 Release</a> 提供正式签名公证安装包。大鱼站仓库作为备份保留:<a href="https://dayu.media/git/dayu/paintfcp-fxplug" target="_blank" rel="noreferrer noopener">paintfcp-fxplug</a>(该入口仍受站点登录与会员网关限制)。也可下载 <a href="{{source_url}}">1.3.1 源码快照 ZIP</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 -->
|
||||||
|
|
||||||
|
<!-- wp:image {"id":15597,"sizeSlug":"large","linkDestination":"none"} -->
|
||||||
|
<figure class="wp-block-image size-large"><img src="https://dayu.media/wp-content/uploads/2026/10/1792605142-78805a221a988e7-359x1024.png" alt="" class="wp-image-15597" /></figure>
|
||||||
|
<!-- /wp:image -->
|
||||||
二进制文件未显示。
@@ -0,0 +1,99 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Update the existing Dayu release article, retaining its cover and access rules.
|
||||||
|
Only --publish performs writes. Credentials stay in memory and go only to Dayu.
|
||||||
|
"""
|
||||||
|
import argparse, base64, hashlib, html, json, pathlib, ssl, subprocess, urllib.parse, urllib.request, uuid, zipfile
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument('--version', required=True)
|
||||||
|
parser.add_argument('--post-id', type=int, default=15593)
|
||||||
|
parser.add_argument('--publish', action='store_true')
|
||||||
|
args = parser.parse_args()
|
||||||
|
root = pathlib.Path(__file__).resolve().parent.parent
|
||||||
|
release = root / 'releases' / args.version
|
||||||
|
work = root / 'build' / ('publish-' + args.version)
|
||||||
|
work.mkdir(parents=True, exist_ok=True)
|
||||||
|
state_path = work / 'operation.json'
|
||||||
|
state = json.loads(state_path.read_text()) if state_path.exists() else {'idempotencyKey': str(uuid.uuid4())}
|
||||||
|
def save():
|
||||||
|
state_path.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/v2/', '/dayu-publisher/v1/')) 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'
|
||||||
|
with opener.open(urllib.request.Request('https://dayu.media/wp-json' + path, data=body, headers=h, method=method), timeout=180) as response:
|
||||||
|
return json.load(response)
|
||||||
|
def public_bytes(url):
|
||||||
|
assert urllib.parse.urlparse(url).hostname == 'dayu.media'
|
||||||
|
with urllib.request.urlopen(url, context=ssl.create_default_context(cafile='/etc/ssl/cert.pem'), timeout=180) as response:
|
||||||
|
return response.read()
|
||||||
|
digest = lambda data: hashlib.sha256(data).hexdigest()
|
||||||
|
route = '/dayu-publisher/v1/posts/' + str(args.post_id)
|
||||||
|
status = request('/dayu-publisher/v1/status')
|
||||||
|
assert status['can_publish'] and status['can_upload'] and 'seo' in status['fields']
|
||||||
|
before = request(route)['article']
|
||||||
|
assert before['id'] == args.post_id and before['status'] == 'publish'
|
||||||
|
if not args.publish:
|
||||||
|
(work / 'article-before.json').write_text(json.dumps({'article': before}, ensure_ascii=False, indent=2) + '\n')
|
||||||
|
print(json.dumps({'id': before['id'], 'url': before['url'], 'status': before['status'], 'writePerformed': False}))
|
||||||
|
raise SystemExit(0)
|
||||||
|
baseline = json.loads((work / 'article-before.json').read_text())['article']
|
||||||
|
payload = json.loads((release / 'article-payload.json').read_text())
|
||||||
|
package = release / ('大鱼人像美化-' + args.version + '.pkg')
|
||||||
|
source = root / 'build' / ('Dayu-Portrait-' + args.version + '-source.zip')
|
||||||
|
with zipfile.ZipFile(source) as z:
|
||||||
|
assert z.testzip() is None and any(n.endswith('/Wrapper Application/AppDelegate.m') for n in z.namelist())
|
||||||
|
assert not any(n.endswith(('.app', '.pkg', '.p12', '.key', '.env')) for n in z.namelist())
|
||||||
|
operation_digest = digest(package.read_bytes() + source.read_bytes() + json.dumps(payload, sort_keys=True, ensure_ascii=False).encode())
|
||||||
|
assert state.get('digest', operation_digest) == operation_digest, 'Prepared release changed; use a new reviewed operation state'
|
||||||
|
state['digest'] = operation_digest
|
||||||
|
save()
|
||||||
|
if not state.get('response'):
|
||||||
|
assert before == baseline, 'Article changed since review; reconcile the current article first'
|
||||||
|
if 'source' not in state:
|
||||||
|
title = 'Dayu Portrait ' + args.version + ' source'
|
||||||
|
matches = request('/wp/v2/media?context=edit&per_page=100&search=' + urllib.parse.quote(title))
|
||||||
|
for item in matches:
|
||||||
|
if item['title'].get('raw', item['title'].get('rendered')) == title and digest(public_bytes(item['source_url'])) == digest(source.read_bytes()):
|
||||||
|
state['source'] = {'id': item['id'], 'url': item['source_url']}
|
||||||
|
break
|
||||||
|
if 'source' not in state:
|
||||||
|
item = request('/wp/v2/media?title=' + urllib.parse.quote(title), 'POST', source.read_bytes(), {'Content-Type': 'application/zip', 'Content-Disposition': 'attachment; filename="' + source.name + '"'})
|
||||||
|
state['source'] = {'id': item['id'], 'url': item['source_url']}
|
||||||
|
save()
|
||||||
|
assert digest(public_bytes(state['source']['url'])) == digest(source.read_bytes())
|
||||||
|
payload['content'] = payload['content'].replace('{{source_url}}', html.escape(state['source']['url'], quote=True))
|
||||||
|
assert '{{' not in payload['content']
|
||||||
|
if not state.get('response'):
|
||||||
|
boundary = 'DayuRelease' + state['idempotencyKey'].replace('-', '')
|
||||||
|
parts = []
|
||||||
|
for name, data, filename, mime in [('payload', json.dumps(payload, ensure_ascii=False).encode(), None, 'application/json'), ('package', package.read_bytes(), 'Dayu-Portrait-' + args.version + '.pkg', 'application/octet-stream')]:
|
||||||
|
head = '--' + boundary + '\r\nContent-Disposition: form-data; name="' + name + '"'
|
||||||
|
if filename:
|
||||||
|
head += '; filename="' + filename + '"'
|
||||||
|
parts.append((head + '\r\nContent-Type: ' + mime + '\r\n\r\n').encode() + data + b'\r\n')
|
||||||
|
state['response'] = request(route, 'POST', b''.join(parts) + ('--' + boundary + '--\r\n').encode(), {'Content-Type': 'multipart/form-data; boundary=' + boundary, 'Idempotency-Key': state['idempotencyKey']})
|
||||||
|
save()
|
||||||
|
after = request(route)['article']
|
||||||
|
for key in ['author', 'categories', 'tags', 'featured_media']:
|
||||||
|
assert after[key] == baseline[key], key
|
||||||
|
for key, value in baseline['resource'].items():
|
||||||
|
if key not in ['cao_downurl', 'cao_original_filename', 'cao_info']:
|
||||||
|
assert after['resource'][key] == value, key
|
||||||
|
assert after['status'] == 'publish' and after['title'] == payload['title']
|
||||||
|
assert after['seo'] == payload['seo'] and after['resource']['cao_info'] == payload['resource']['cao_info']
|
||||||
|
assert after['content'].count('<!-- wp:code -->') == 1
|
||||||
|
assert state['source']['url'] in after['content'] and ('/releases/tag/v' + args.version) in after['content']
|
||||||
|
assert digest(public_bytes(after['resource']['cao_downurl'])) == digest(package.read_bytes())
|
||||||
|
(work / 'article-after.json').write_text(json.dumps({'article': after}, ensure_ascii=False, indent=2) + '\n')
|
||||||
|
print(json.dumps({'id': after['id'], 'url': after['url'], 'status': after['status'], 'packageHashVerified': True, 'sourceHashVerified': True, 'coverAndAccessPreserved': True}, ensure_ascii=False))
|
||||||
@@ -1,9 +1,15 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Verify an expanded release payload and run template-only update/rollback in isolation."""
|
"""Verify an expanded release payload and run template-only update/rollback in isolation."""
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import hashlib,json,os,plistlib,shutil,subprocess,sys
|
import argparse,hashlib,json,os,plistlib,shutil,subprocess,sys
|
||||||
root=Path(__file__).resolve().parent.parent
|
root=Path(__file__).resolve().parent.parent
|
||||||
expanded=root/'build/pkg-expanded/Payload.pkg';payload=expanded/'Payload'
|
parser=argparse.ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument('--version',default='1.2.0')
|
||||||
|
parser.add_argument('--build',default='14')
|
||||||
|
parser.add_argument('--expanded',default='build/pkg-expanded')
|
||||||
|
parser.add_argument('--fixture',default='build/pkg-isolation')
|
||||||
|
args=parser.parse_args()
|
||||||
|
expanded=root/args.expanded/'Payload.pkg';payload=expanded/'Payload'
|
||||||
formal=Path('/Library/Plug-Ins/FxPlug/beatygril.app');packed=payload/'Library/Plug-Ins/FxPlug/beatygril.app'
|
formal=Path('/Library/Plug-Ins/FxPlug/beatygril.app');packed=payload/'Library/Plug-Ins/FxPlug/beatygril.app'
|
||||||
sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest()
|
sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest()
|
||||||
def files(p):
|
def files(p):
|
||||||
@@ -23,7 +29,7 @@ assert len(locks)==2 and any('/large.png' in l for l in locks) and any('/small.p
|
|||||||
assert 'Template Backups' in post and 'restore_original_locks' in post
|
assert 'Template Backups' in post and 'restore_original_locks' in post
|
||||||
# Keep the exact generated functions and template body. Only identity lookup,
|
# Keep the exact generated functions and template body. Only identity lookup,
|
||||||
# chown and external FxPlug registration are replaced to confine execution.
|
# chown and external FxPlug registration are replaced to confine execution.
|
||||||
base=root/'build/pkg-isolation';base.mkdir(exist_ok=False)
|
base=root/args.fixture;base.mkdir(exist_ok=False)
|
||||||
volume=base/'volume';home=base/'user';home.mkdir()
|
volume=base/'volume';home=base/'user';home.mkdir()
|
||||||
start=post.index('CURRENT_ACTIVE=0')
|
start=post.index('CURRENT_ACTIVE=0')
|
||||||
isolated='#!/bin/zsh\nset -eu\nTARGET_VOLUME="'+str(volume)+'"\nCONSOLE_USER="'+os.getlogin()+'"\nCONSOLE_UID="'+str(os.getuid())+'"\nCONSOLE_GROUP="staff"\nUSER_HOME="'+str(home)+'"\n'+post[start:]
|
isolated='#!/bin/zsh\nset -eu\nTARGET_VOLUME="'+str(volume)+'"\nCONSOLE_USER="'+os.getlogin()+'"\nCONSOLE_UID="'+str(os.getuid())+'"\nCONSOLE_GROUP="staff"\nUSER_HOME="'+str(home)+'"\n'+post[start:]
|
||||||
@@ -60,8 +66,10 @@ assert files(final)==original
|
|||||||
assert all((final/n).stat().st_flags & 2 for n in ['large.png','small.png'])
|
assert all((final/n).stat().st_flags & 2 for n in ['large.png','small.png'])
|
||||||
backups=list((home/'Library/Application Support/PKGForge/Template Backups').rglob('.locked-paths'))
|
backups=list((home/'Library/Application Support/PKGForge/Template Backups').rglob('.locked-paths'))
|
||||||
assert backups and all('/Movies/' not in str(x) for x in backups)
|
assert backups and all('/Movies/' not in str(x) for x in backups)
|
||||||
package=root/'releases/1.2.0/大鱼人像美化-1.2.0.pkg'
|
package=root/('releases/'+args.version+'/大鱼人像美化-'+args.version+'.pkg')
|
||||||
report={'package':str(package.relative_to(root)),'bytes':package.stat().st_size,'sha256':sha(package),'version':'1.2.0','build':'14','app_files_compared':len(files(formal)),'app_byte_and_symlink_equality':True,'template_files':{n:sha(template/n) for n in runtime},'locked_relative_paths':['大鱼人像美化/large.png','大鱼人像美化/small.png'],'script_sha256':{'preinstall':sha(expanded/'Scripts/preinstall'),'postinstall':sha(expanded/'Scripts/postinstall')},'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.'}
|
info=plistlib.loads((packed/'Contents/Info.plist').read_bytes())
|
||||||
(root/'docs/verification/v1.2.0/pkg-verification.json').write_text(json.dumps(report,ensure_ascii=False,indent=2)+'\n');print(json.dumps(report,ensure_ascii=False,indent=2))
|
assert info['CFBundleShortVersionString']==args.version and info['CFBundleVersion']==args.build
|
||||||
|
report={'package':str(package.relative_to(root)),'bytes':package.stat().st_size,'sha256':sha(package),'version':args.version,'build':args.build,'app_files_compared':len(files(formal)),'app_byte_and_symlink_equality':True,'template_files':{n:sha(template/n) for n in runtime},'locked_relative_paths':['大鱼人像美化/large.png','大鱼人像美化/small.png'],'script_sha256':{'preinstall':sha(expanded/'Scripts/preinstall'),'postinstall':sha(expanded/'Scripts/postinstall')},'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; formal template files matched the specified installed category, not changed by running this PKG','fonts':'No editable third-party font dependency; promotional lettering baked into PNG. No optional font ZIP needed.'}
|
||||||
|
(root/('docs/verification/v'+args.version+'/pkg-verification.json')).write_text(json.dumps(report,ensure_ascii=False,indent=2)+'\n');print(json.dumps(report,ensure_ascii=False,indent=2))
|
||||||
# This fixture belongs solely to this verifier; remove its locks before cleanup.
|
# This fixture belongs solely to this verifier; remove its locks before cleanup.
|
||||||
subprocess.run(['chflags','-R','nouchg',str(base)],check=True);shutil.rmtree(base)
|
subprocess.run(['chflags','-R','nouchg',str(base)],check=True);shutil.rmtree(base)
|
||||||
@@ -103,6 +103,46 @@ int main(void) { @autoreleasepool {
|
|||||||
s=PGBeautyDefaults();s.midtoneDetail=1;s.maskSoftness=0;s.maskEdgeProtect=0;s.featureProtection=1;
|
s=PGBeautyDefaults();s.midtoneDetail=1;s.maskSoftness=0;s.maskEdgeProtect=0;s.featureProtection=1;
|
||||||
float *protected=Render(context,[renderer processImage:source settings:s skinMask:white teethMask:nil eyeMask:half error:&error],extent);
|
float *protected=Render(context,[renderer processImage:source settings:s skinMask:white teethMask:nil eyeMask:half error:&error],extent);
|
||||||
Check(Difference(base,protected,6,40)<0.00001 && Texture(protected,56,88)<Texture(base,56,88)*0.82,@"Feature protection retains eye detail while smoothing nearby skin");free(protected);
|
Check(Difference(base,protected,6,40)<0.00001 && Texture(protected,56,88)<Texture(base,56,88)*0.82,@"Feature protection retains eye detail while smoothing nearby skin");free(protected);
|
||||||
|
// Independent protection controls use fractional coverage; neither changes
|
||||||
|
// exposure coverage nor invents smoothing when the smoothing amount is zero.
|
||||||
|
s=PGBeautyDefaults();s.midtoneDetail=1;s.maskSoftness=0;s.maskEdgeProtect=0;
|
||||||
|
float *featureFull=Render(context,[renderer processImage:source settings:s skinMask:white teethMask:nil eyeMask:half error:&error],extent);
|
||||||
|
s.featureProtectionAmount=.5;
|
||||||
|
float *featureHalf=Render(context,[renderer processImage:source settings:s skinMask:white teethMask:nil eyeMask:half error:&error],extent);
|
||||||
|
s.featureProtectionAmount=0;
|
||||||
|
float *featureNone=Render(context,[renderer processImage:source settings:s skinMask:white teethMask:nil eyeMask:half error:&error],extent);
|
||||||
|
float fullDelta=Difference(base,featureFull,6,40),halfDelta=Difference(base,featureHalf,6,40),noneDelta=Difference(base,featureNone,6,40);
|
||||||
|
Check(fullDelta<.00001 && halfDelta>fullDelta+.001 && noneDelta>halfDelta+.001,@"Five-feature protection has distinct ordered 0/50/100 percent pixel effects");
|
||||||
|
Check(Difference(featureFull,featureNone,56,88)<noneDelta*.25,@"Feature slider primarily changes marked features rather than nearby skin texture");
|
||||||
|
free(featureFull);free(featureHalf);free(featureNone);
|
||||||
|
s=PGBeautyDefaults();s.midtoneDetail=1;s.maskSoftness=0;s.maskEdgeProtect=0;s.featureProtection=0;
|
||||||
|
float *skinFull=Render(context,[renderer processImage:source settings:s skinMask:half teethMask:nil eyeMask:nil error:&error],extent);
|
||||||
|
s.nonSkinProtection=.5;
|
||||||
|
float *skinHalf=Render(context,[renderer processImage:source settings:s skinMask:half teethMask:nil eyeMask:nil error:&error],extent);
|
||||||
|
s.nonSkinProtection=0;
|
||||||
|
float *skinNone=Render(context,[renderer processImage:source settings:s skinMask:half teethMask:nil eyeMask:nil error:&error],extent);
|
||||||
|
Check(Difference(base,skinFull,56,88)<.00001 && Difference(base,skinHalf,56,88)>.001 && Difference(base,skinNone,56,88)>Difference(base,skinHalf,56,88)+.001,@"Non-skin protection progressively admits background into the local smoothing mask");
|
||||||
|
Check(Difference(skinFull,skinNone,6,40)<.00001,@"Non-skin protection leaves already-covered skin strength unchanged");
|
||||||
|
free(skinFull);free(skinHalf);free(skinNone);
|
||||||
|
s.midtoneDetail=0;
|
||||||
|
float *noSmoothing=Render(context,[renderer processImage:source settings:s skinMask:half teethMask:nil eyeMask:nil error:&error],extent);
|
||||||
|
Check(Difference(base,noSmoothing,4,W-4)<.00001,@"Releasing non-skin protection alone does not enable an independent global smoothing effect");free(noSmoothing);
|
||||||
|
s=PGBeautyDefaults();s.microDetail=1;s.maskSoftness=0;s.maskEdgeProtect=0;
|
||||||
|
float *micro=Render(context,[renderer processImage:source settings:s skinMask:half teethMask:nil eyeMask:nil error:&error],extent);
|
||||||
|
Check(Texture(micro,6,40)>Texture(base,6,40)*1.05 && Difference(base,micro,56,88)<.00001,@"Micro detail recovers texture within smoothing support, including without smoothing");
|
||||||
|
free(micro);
|
||||||
|
s=PGBeautyDefaults();s.skinBrightness=.7;s.maskSoftness=0;s.maskEdgeProtect=0;
|
||||||
|
float *colorProtected=Render(context,[renderer processImage:source settings:s skinMask:half colorMask:half teethMask:nil eyeMask:nil error:&error],extent);
|
||||||
|
s.nonSkinProtection=0;s.featureProtectionAmount=0;
|
||||||
|
float *colorReleased=Render(context,[renderer processImage:source settings:s skinMask:half colorMask:half teethMask:nil eyeMask:nil error:&error],extent);
|
||||||
|
Check(Difference(colorProtected,colorReleased,4,W-4)<.00001,@"Both new protection sliders leave skin exposure coverage unchanged");
|
||||||
|
free(colorProtected);free(colorReleased);
|
||||||
|
s=PGBeautyDefaults();s.skinBrightness=.7;s.maskSoftness=0;s.maskEdgeProtect=0;
|
||||||
|
colorProtected=Render(context,[renderer processImage:source settings:s skinMask:white teethMask:half eyeMask:half error:&error],extent);
|
||||||
|
s.featureProtectionAmount=0;s.featureProtection=0;s.nonSkinProtection=0;
|
||||||
|
colorReleased=Render(context,[renderer processImage:source settings:s skinMask:white teethMask:half eyeMask:half error:&error],extent);
|
||||||
|
Check(Difference(colorProtected,colorReleased,4,W-4)<.00001,@"Standalone renderer also keeps eye/teeth protection independent from exposure when the legacy bool changes");
|
||||||
|
free(colorProtected);free(colorReleased);
|
||||||
// A skin exposure adjustment must stay continuous across detail-protection
|
// A skin exposure adjustment must stay continuous across detail-protection
|
||||||
// holes. This models the eye/nose islands reported on the real beach clip.
|
// holes. This models the eye/nose islands reported on the real beach clip.
|
||||||
s=PGBeautyDefaults();s.skinBrightness=1;s.featureProtection=1;
|
s=PGBeautyDefaults();s.skinBrightness=1;s.featureProtection=1;
|
||||||
@@ -192,6 +232,7 @@ int main(void) { @autoreleasepool {
|
|||||||
// Every operation must preserve transparency rather than creating halos/opaque pixels.
|
// Every operation must preserve transparency rather than creating halos/opaque pixels.
|
||||||
for(int i=0;i<W*H;i++){p[i*4]*=0.4;p[i*4+1]*=0.4;p[i*4+2]*=0.4;p[i*4+3]=0.4;}
|
for(int i=0;i<W*H;i++){p[i*4]*=0.4;p[i*4+1]*=0.4;p[i*4+2]*=0.4;p[i*4+3]=0.4;}
|
||||||
CIImage *transparent=Image(p,extent);s=PGBeautyDefaults();s.midtoneDetail=1;s.skinBrightness=1;s.teethWhitening=1;s.eyeBrightening=1;s.skinTonePreset=3;s.highPassDetail=1;
|
CIImage *transparent=Image(p,extent);s=PGBeautyDefaults();s.midtoneDetail=1;s.skinBrightness=1;s.teethWhitening=1;s.eyeBrightening=1;s.skinTonePreset=3;s.highPassDetail=1;
|
||||||
|
s.microDetail=.7;s.nonSkinProtection=.3;s.featureProtectionAmount=.4;
|
||||||
float *alpha=Render(context,[renderer processImage:transparent settings:s skinMask:white teethMask:half eyeMask:half error:&error],extent);
|
float *alpha=Render(context,[renderer processImage:transparent settings:s skinMask:white teethMask:half eyeMask:half error:&error],extent);
|
||||||
BOOL alphaOK=YES,finite=YES;
|
BOOL alphaOK=YES,finite=YES;
|
||||||
for(int i=0;i<W*H;i++){if(fabsf(alpha[i*4+3]-0.4f)>0.0002)alphaOK=NO;for(int c=0;c<4;c++)if(!isfinite(alpha[i*4+c]))finite=NO;}
|
for(int i=0;i<W*H;i++){if(fabsf(alpha[i*4+3]-0.4f)>0.0002)alphaOK=NO;for(int c=0;c<4;c++)if(!isfinite(alpha[i*4+c]))finite=NO;}
|
||||||
|
|||||||
+2
-2
@@ -323,8 +323,8 @@ int main(int argc, const char *argv[]) {
|
|||||||
*controls[i] = 1;
|
*controls[i] = 1;
|
||||||
PGFaceFrame *warped = [processor processImage:image settings:changed time:kCMTimeZero error:&error];
|
PGFaceFrame *warped = [processor processImage:image settings:changed time:kCMTimeZero error:&error];
|
||||||
BOOL changedPixels=![Pixels(context, warped.image) isEqualToData:base];
|
BOOL changedPixels=![Pixels(context, warped.image) isEqualToData:base];
|
||||||
BOOL requiresPoints=(i==0 || i==2 || i==4 || i==6);
|
BOOL requiresPoints=(i==0 || i==2 || i==4 || i==5 || i==6);
|
||||||
Check(warped != nil && (requiresPoints ? !changedPixels : changedPixels), requiresPoints ? @"recovered thin/chin/short/jaw skip a face with no indexed landmarks" : [@"geometry control changes pixels: " stringByAppendingString:names[i]]);
|
Check(warped != nil && (requiresPoints ? !changedPixels : changedPixels), requiresPoints ? @"recovered thin/V/chin/short/jaw skip a face with no indexed landmarks" : [@"geometry control changes pixels: " stringByAppendingString:names[i]]);
|
||||||
Check(CGRectEqualToRect(warped.image.extent, image.extent), @"warp preserves image extent");
|
Check(CGRectEqualToRect(warped.image.extent, image.extent), @"warp preserves image extent");
|
||||||
}
|
}
|
||||||
settings.faceShort = 0.8;
|
settings.faceShort = 0.8;
|
||||||
|
|||||||
+15
-6
@@ -337,15 +337,16 @@ static void PortraitAnatomyTests(NSString *fixture, CIImage *portrait) {
|
|||||||
}
|
}
|
||||||
lowerFace = CGRectInset(lowerFace, -fw*.15, -fh*.15);
|
lowerFace = CGRectInset(lowerFace, -fw*.15, -fh*.15);
|
||||||
CGRect upsideDownFace = CGRectMake(box.origin.x*width, box.origin.y*height, fw, fh);
|
CGRect upsideDownFace = CGRectMake(box.origin.x*width, box.origin.y*height, fw, fh);
|
||||||
const UInt32 controls[] = {0, PGBigEye, PGChin, PGChin, PGShort, PGShort, PGJaw};
|
const UInt32 controls[] = {0, PGBigEye, PGChin, PGChin, PGShort, PGShort, PGJaw, PGVFace};
|
||||||
const double values[] = {0, 1, 1, -1, 1, -1, 1};
|
const double values[] = {0, 1, 1, -1, 1, -1, 1, 1};
|
||||||
NSArray<NSString *> *names = @[@"default", @"big-eye", @"chin-positive", @"chin-negative", @"short-positive", @"short-negative", @"jaw"];
|
NSArray<NSString *> *names = @[@"default", @"big-eye", @"chin-positive", @"chin-negative", @"short-positive", @"short-negative", @"jaw", @"v-face"];
|
||||||
for (NSUInteger effect = 0; effect < names.count; effect++) {
|
for (NSUInteger effect = 0; effect < names.count; effect++) {
|
||||||
PGBeautySettings settings = PGBeautyDefaults();
|
PGBeautySettings settings = PGBeautyDefaults();
|
||||||
if (effect == 1) settings.faceBigEye = 1;
|
if (effect == 1) settings.faceBigEye = 1;
|
||||||
else if (effect == 2 || effect == 3) settings.faceChin = values[effect];
|
else if (effect == 2 || effect == 3) settings.faceChin = values[effect];
|
||||||
else if (effect == 4 || effect == 5) settings.faceShort = values[effect];
|
else if (effect == 4 || effect == 5) settings.faceShort = values[effect];
|
||||||
else if (effect == 6) settings.faceJaw = values[effect];
|
else if (effect == 6) settings.faceJaw = values[effect];
|
||||||
|
else if (effect == 7) settings.faceV = values[effect];
|
||||||
NSData *expected = ImageBytes(context, ProcessedReference(portrait, settings, kCMTimeZero, nil));
|
NSData *expected = ImageBytes(context, ProcessedReference(portrait, settings, kCMTimeZero, nil));
|
||||||
Check(expected.length == canonical.length, [names[effect] stringByAppendingString:@" independent upright face/beauty chain renders"]);
|
Check(expected.length == canonical.length, [names[effect] stringByAppendingString:@" independent upright face/beauty chain renders"]);
|
||||||
if (expected.length != canonical.length) continue;
|
if (expected.length != canonical.length) continue;
|
||||||
@@ -469,7 +470,9 @@ int main(int argc, const char *argv[]) {
|
|||||||
Check([plugin addParametersWithError:&error] && !error, @"host creates complete parameter set");
|
Check([plugin addParametersWithError:&error] && !error, @"host creates complete parameter set");
|
||||||
UInt32 identifiers[] = {PGAmount,PGBrightness,PGRadius,PGSharpness,PGMix,PGThreshold,PGSoftness,PGEdgeProtect,PGShowMask,PGMidtone,PGHue,PGSaturation,PGHighPass,PGThin,PGFaceBoxes,PGSingleFace,PGSelection,PGShort,PGNarrow,PGChin,PGVFace,PGJaw,PGCheekbone,PGSmallHead,PGSmallHeadRange,PGFeatureProtect,PGTeeth,PGEyeBright,PGBigEye,PGTrackBoxes,PGFastMulti,PGJumpGuard,PGEvenTone,PGTonePreset,PGInvertMask,PGDebugChannel,PGTrackEnabled,PGTarget,PGAnalyzeForward,PGAnalyzeBackward,PGClearTrack,PGStatus,PGTrackData1,PGTrackData2,PGTrackData3,PGSmoothingEnabled,PGColorEnabled,PGShapeEnabled,PGDetailEnabled,PGInspectorPanel};
|
UInt32 identifiers[] = {PGAmount,PGBrightness,PGRadius,PGSharpness,PGMix,PGThreshold,PGSoftness,PGEdgeProtect,PGShowMask,PGMidtone,PGHue,PGSaturation,PGHighPass,PGThin,PGFaceBoxes,PGSingleFace,PGSelection,PGShort,PGNarrow,PGChin,PGVFace,PGJaw,PGCheekbone,PGSmallHead,PGSmallHeadRange,PGFeatureProtect,PGTeeth,PGEyeBright,PGBigEye,PGTrackBoxes,PGFastMulti,PGJumpGuard,PGEvenTone,PGTonePreset,PGInvertMask,PGDebugChannel,PGTrackEnabled,PGTarget,PGAnalyzeForward,PGAnalyzeBackward,PGClearTrack,PGStatus,PGTrackData1,PGTrackData2,PGTrackData3,PGSmoothingEnabled,PGColorEnabled,PGShapeEnabled,PGDetailEnabled,PGInspectorPanel};
|
||||||
for (NSUInteger i = 0; i < sizeof(identifiers) / sizeof(UInt32); i++) Check(api.definitions[@(identifiers[i])] != nil, [NSString stringWithFormat:@"required parameter %u exists", identifiers[i]]);
|
for (NSUInteger i = 0; i < sizeof(identifiers) / sizeof(UInt32); i++) Check(api.definitions[@(identifiers[i])] != nil, [NSString stringWithFormat:@"required parameter %u exists", identifiers[i]]);
|
||||||
Check(api.depth == 0 && !api.duplicate && api.definitions.count == sizeof(identifiers) / sizeof(UInt32) + 6, @"parameter groups close and IDs are unique");
|
for(NSNumber *identifier in @[@(PGFeatureProtectionAmount),@(PGNonSkinProtection),@(PGMicroDetail)])
|
||||||
|
Check(api.definitions[identifier]!=nil,@"new protection/detail float parameter is registered");
|
||||||
|
Check(api.depth == 0 && !api.duplicate && api.definitions.count == sizeof(identifiers) / sizeof(UInt32) + 10, @"parameter groups close and IDs are unique");
|
||||||
Check([api.values[@(PGMidtone)] doubleValue] == 0 && [api.values[@(PGMix)] doubleValue] == 1, @"default detail and mix form identity filter");
|
Check([api.values[@(PGMidtone)] doubleValue] == 0 && [api.values[@(PGMix)] doubleValue] == 1, @"default detail and mix form identity filter");
|
||||||
Check([api.definitions[@(PGShort)][@"min"] doubleValue] == -1 && [api.definitions[@(PGShort)][@"max"] doubleValue] == 1, @"signed face control exposes both directions");
|
Check([api.definitions[@(PGShort)][@"min"] doubleValue] == -1 && [api.definitions[@(PGShort)][@"max"] doubleValue] == 1, @"signed face control exposes both directions");
|
||||||
Check([api.definitions[@(PGTrackData1)][@"flags"] unsignedLongLongValue] & kFxParameterFlag_HIDDEN, @"persistent tracking data stays hidden in host UI");
|
Check([api.definitions[@(PGTrackData1)][@"flags"] unsignedLongLongValue] & kFxParameterFlag_HIDDEN, @"persistent tracking data stays hidden in host UI");
|
||||||
@@ -480,7 +483,7 @@ int main(int argc, const char *argv[]) {
|
|||||||
NSDictionary *decoded = [NSPropertyListSerialization propertyListWithData:state options:0 format:nil error:nil];
|
NSDictionary *decoded = [NSPropertyListSerialization propertyListWithData:state options:0 format:nil error:nil];
|
||||||
Check([decoded[@"tracks"] count] == 3 && [decoded[@"settings"] length] == sizeof(PGBeautySettings), @"snapshot contains fixed settings schema and three target slots");
|
Check([decoded[@"tracks"] count] == 3 && [decoded[@"settings"] length] == sizeof(PGBeautySettings), @"snapshot contains fixed settings schema and three target slots");
|
||||||
PGBeautySettings settings; [decoded[@"settings"] getBytes:&settings length:sizeof(settings)];
|
PGBeautySettings settings; [decoded[@"settings"] getBytes:&settings length:sizeof(settings)];
|
||||||
Check(settings.version == 1 && settings.mix == 1 && settings.midtoneDetail == 0, @"snapshot round-trips default numeric state");
|
Check(settings.version == PGBeautySettingsVersion && settings.mix == 1 && settings.midtoneDetail == 0 && settings.featureProtectionAmount==1 && settings.nonSkinProtection==1 && settings.microDetail==0, @"snapshot round-trips v2 with neutral defaults for the new controls");
|
||||||
api.values[@(PGThin)] = @5; api.values[@(PGRadius)] = @(-9); api.values[@(PGSelection)] = @[@(-1), @2];
|
api.values[@(PGThin)] = @5; api.values[@(PGRadius)] = @(-9); api.values[@(PGSelection)] = @[@(-1), @2];
|
||||||
settings = [plugin settingsAtTime:api.hostTime];
|
settings = [plugin settingsAtTime:api.hostTime];
|
||||||
Check(settings.faceThin == 1 && settings.radius == 1 && settings.selectionX == 0 && settings.selectionY == 1, @"host parameter limits and normalized selection are enforced");
|
Check(settings.faceThin == 1 && settings.radius == 1 && settings.selectionX == 0 && settings.selectionY == 1, @"host parameter limits and normalized selection are enforced");
|
||||||
@@ -493,6 +496,12 @@ int main(int argc, const char *argv[]) {
|
|||||||
NSUInteger difference = MaximumDifference(SurfaceBytes(source), SurfaceBytes(destination));
|
NSUInteger difference = MaximumDifference(SurfaceBytes(source), SurfaceBytes(destination));
|
||||||
printf("Default IOSurface maximum byte difference: %lu\n", (unsigned long)difference);
|
printf("Default IOSurface maximum byte difference: %lu\n", (unsigned long)difference);
|
||||||
Check(difference <= 1, @"default adapter output preserves gamma-video numeric samples");
|
Check(difference <= 1, @"default adapter output preserves gamma-video numeric samples");
|
||||||
|
PGBeautySettings old=PGBeautyDefaults();old.version=1;
|
||||||
|
NSData *legacy=PlistState(@{@"settings":[NSData dataWithBytes:&old length:PGBeautySettingsV1Size],@"tracks":@[@"",@"",@""]});
|
||||||
|
Check(Render(plugin,destination,@[source],legacy,kCMTimeZero,&error,NULL) && MaximumDifference(SurfaceBytes(source),SurfaceBytes(destination))<=1,@"160-byte v1 snapshots migrate with neutral new controls and preserve original pixels");
|
||||||
|
RejectState(plugin,destination,source,PlistState(@{@"settings":[NSData dataWithBytes:&old length:sizeof(old)],@"tracks":@[@"",@"",@""]}),@"v1 with the new structure length is rejected instead of guessing its layout");
|
||||||
|
old.version=PGBeautySettingsVersion;
|
||||||
|
RejectState(plugin,destination,source,PlistState(@{@"settings":[NSData dataWithBytes:&old length:PGBeautySettingsV1Size],@"tracks":@[@"",@"",@""]}),@"v2 with a truncated legacy-sized payload is rejected");
|
||||||
CGColorSpaceRelease(destination.space); destination.space = CGColorSpaceCreateWithName(kCGColorSpaceLinearSRGB);
|
CGColorSpaceRelease(destination.space); destination.space = CGColorSpaceCreateWithName(kCGColorSpaceLinearSRGB);
|
||||||
Check(Render(plugin, destination, @[source], state, kCMTimeZero, &error, NULL), @"numeric host render accepts destination color-space metadata");
|
Check(Render(plugin, destination, @[source], state, kCMTimeZero, &error, NULL), @"numeric host render accepts destination color-space metadata");
|
||||||
difference = MaximumDifference(SurfaceBytes(source), SurfaceBytes(destination));
|
difference = MaximumDifference(SurfaceBytes(source), SurfaceBytes(destination));
|
||||||
@@ -510,7 +519,7 @@ int main(int argc, const char *argv[]) {
|
|||||||
RejectState(plugin, destination, source, PlistState(@{@"settings":[NSData data], @"tracks":@[@"",@"",@""]}), @"wrong settings size is rejected safely");
|
RejectState(plugin, destination, source, PlistState(@{@"settings":[NSData data], @"tracks":@[@"",@"",@""]}), @"wrong settings size is rejected safely");
|
||||||
settings = PGBeautyDefaults(); settings.version = 99;
|
settings = PGBeautyDefaults(); settings.version = 99;
|
||||||
RejectState(plugin, destination, source, PlistState(@{@"settings":[NSData dataWithBytes:&settings length:sizeof(settings)], @"tracks":@[@"",@"",@""]}), @"unknown saved settings version is rejected safely");
|
RejectState(plugin, destination, source, PlistState(@{@"settings":[NSData dataWithBytes:&settings length:sizeof(settings)], @"tracks":@[@"",@"",@""]}), @"unknown saved settings version is rejected safely");
|
||||||
settings.version = 1;
|
settings.version = PGBeautySettingsVersion;
|
||||||
RejectState(plugin, destination, source, PlistState(@{@"settings":[NSData dataWithBytes:&settings length:sizeof(settings)], @"tracks":@[@"",@""]}), @"wrong target slot count is rejected safely");
|
RejectState(plugin, destination, source, PlistState(@{@"settings":[NSData dataWithBytes:&settings length:sizeof(settings)], @"tracks":@[@"",@""]}), @"wrong target slot count is rejected safely");
|
||||||
RejectState(plugin, destination, source, PlistState(@{@"settings":[NSData dataWithBytes:&settings length:sizeof(settings)], @"tracks":@[@"",@7,@""]}), @"non-string target slot is rejected safely");
|
RejectState(plugin, destination, source, PlistState(@{@"settings":[NSData dataWithBytes:&settings length:sizeof(settings)], @"tracks":@[@"",@7,@""]}), @"non-string target slot is rejected safely");
|
||||||
for (NSUInteger top = 0; top < 2; top++) {
|
for (NSUInteger top = 0; top < 2; top++) {
|
||||||
|
|||||||
+112
-14
@@ -52,6 +52,9 @@ static NSControl *FindControl(NSView *view,Class type,NSString *label) {
|
|||||||
}
|
}
|
||||||
static void Fire(NSControl *control) {[control sendAction:control.action to:control.target];}
|
static void Fire(NSControl *control) {[control sendAction:control.action to:control.target];}
|
||||||
static BOOL Near(double a,double b) {return fabs(a-b)<.00001;}
|
static BOOL Near(double a,double b) {return fabs(a-b)<.00001;}
|
||||||
|
static PGInspectorState *State(NSDictionary *values,NSSet *disabled) {
|
||||||
|
return [PGInspectorState stateWithValues:values disabledParameters:disabled?:[NSSet set] hasEdits:YES];
|
||||||
|
}
|
||||||
static void RegistrationTests(void) {
|
static void RegistrationTests(void) {
|
||||||
PGHostAPI *api=[PGHostAPI new];beatygrilPlugIn *plugin=[[beatygrilPlugIn alloc]initWithAPIManager:api];[plugin addParametersWithError:nil];
|
PGHostAPI *api=[PGHostAPI new];beatygrilPlugIn *plugin=[[beatygrilPlugIn alloc]initWithAPIManager:api];[plugin addParametersWithError:nil];
|
||||||
NSUInteger visible=0;for(NSNumber *key in api.definitions){NSDictionary *d=api.definitions[key];if(!([d[@"flags"]unsignedIntValue]&kFxParameterFlag_HIDDEN)){visible++;Check(key.unsignedIntValue==PGInspectorPanel,@"only the custom panel is visible in the native parameter list");}}
|
NSUInteger visible=0;for(NSNumber *key in api.definitions){NSDictionary *d=api.definitions[key];if(!([d[@"flags"]unsignedIntValue]&kFxParameterFlag_HIDDEN)){visible++;Check(key.unsignedIntValue==PGInspectorPanel,@"only the custom panel is visible in the native parameter list");}}
|
||||||
@@ -65,11 +68,47 @@ static void RegistrationTests(void) {
|
|||||||
NSNumber *key=d[@"id"];Check(api.definitions[key]!=nil,@"every inspector control maps to an existing stable parameter ID");
|
NSNumber *key=d[@"id"];Check(api.definitions[key]!=nil,@"every inspector control maps to an existing stable parameter ID");
|
||||||
if([d[@"kind"]isEqual:@"slider"])Check(Near([d[@"min"]doubleValue],[api.definitions[key][@"min"]doubleValue]) && Near([d[@"max"]doubleValue],[api.definitions[key][@"max"]doubleValue]),@"UI and renderer enforce the same slider range");
|
if([d[@"kind"]isEqual:@"slider"])Check(Near([d[@"min"]doubleValue],[api.definitions[key][@"min"]doubleValue]) && Near([d[@"max"]doubleValue],[api.definitions[key][@"max"]doubleValue]),@"UI and renderer enforce the same slider range");
|
||||||
}
|
}
|
||||||
|
NSMutableArray *smoothIDs=[NSMutableArray new];
|
||||||
|
for(NSDictionary *d in PGInspectorControls())if([d[@"section"]isEqual:@"smooth"] && ![d[@"kind"]isEqual:@"master"])[smoothIDs addObject:d[@"id"]];
|
||||||
|
Check([smoothIDs isEqual:@[@(PGMidtone),@(PGMicroDetail),@(PGFeatureProtectionAmount),@(PGNonSkinProtection)]],@"primary smoothing card exposes the four new controls in reference order");
|
||||||
|
Check(PGInspectorDescriptor(PGFeatureProtect)==nil && [PGInspectorDescriptor(PGHighPass)[@"section"]isEqual:@"more"] && [PGInspectorDescriptor(PGSharpness)[@"section"]isEqual:@"more"],@"legacy boolean stays hidden while legacy global smoothing and sharpening remain available in more settings");
|
||||||
api.values[@(PGTrackEnabled)]=@YES;api.values[@(PGSingleFace)]=@YES;api.values[@(PGShowMask)]=@YES;
|
api.values[@(PGTrackEnabled)]=@YES;api.values[@(PGSingleFace)]=@YES;api.values[@(PGShowMask)]=@YES;
|
||||||
PGBeautySettings s=[plugin settingsAtTime:kCMTimeZero];Check(!s.trackingEnabled && !s.singleFace && !s.showMask,@"old manual analysis, selection and debug flags cannot override automatic output");
|
PGBeautySettings s=[plugin settingsAtTime:kCMTimeZero];Check(!s.trackingEnabled && !s.singleFace && !s.showMask,@"old manual analysis, selection and debug flags cannot override automatic output");
|
||||||
NSData *snapshot=Snapshot(plugin,kCMTimeZero);NSDictionary *plist=[NSPropertyListSerialization propertyListWithData:snapshot options:0 format:nil error:nil];
|
NSData *snapshot=Snapshot(plugin,kCMTimeZero);NSDictionary *plist=[NSPropertyListSerialization propertyListWithData:snapshot options:0 format:nil error:nil];
|
||||||
Check([plist[@"tracks"]isEqual:@[@"",@"",@""]],@"automatic render snapshots do not transfer persisted manual-analysis payloads");
|
Check([plist[@"tracks"]isEqual:@[@"",@"",@""]],@"automatic render snapshots do not transfer persisted manual-analysis payloads");
|
||||||
}
|
}
|
||||||
|
static void BeautyControlModelTests(void) {
|
||||||
|
PGInspectorState *defaults=State(@{},nil);
|
||||||
|
Check([defaults valueForParameter:PGFeatureProtectionAmount]==1 && [defaults valueForParameter:PGNonSkinProtection]==1 && [defaults valueForParameter:PGMicroDetail]==0,@"new controls default to full five-feature/non-skin protection and zero micro detail");
|
||||||
|
for(NSNumber *legacy in @[@NO,@YES]) {
|
||||||
|
PGInspectorState *migrated=State(@{@(PGFeatureProtect):legacy,@(PGHighPass):@.63},nil);
|
||||||
|
PGBeautySettings s=PGSettingsWithInspectorState(PGBeautyDefaults(),migrated);
|
||||||
|
Check([migrated valueForParameter:PGFeatureProtectionAmount]==legacy.boolValue && s.featureProtection==legacy.boolValue && Near(s.highPassDetail,.63) && s.nonSkinProtection==1,@"old schema dictionaries migrate five-feature bool without reinterpreting legacy global smoothing");
|
||||||
|
}
|
||||||
|
PGInspectorState *explicit=State(@{@(PGFeatureProtect):@NO,@(PGFeatureProtectionAmount):@.37},nil);
|
||||||
|
Check(Near([explicit valueForParameter:PGFeatureProtectionAmount],.37),@"an explicit numeric protection setting takes precedence over the old boolean");
|
||||||
|
NSDictionary *values=@{@(PGFeatureProtectionAmount):@.25,@(PGNonSkinProtection):@.6,@(PGMicroDetail):@.8,@(PGMidtone):@.7,@(PGHighPass):@.4,@(PGSharpness):@.5,@(PGHue):@12,@(PGBrightness):@.3,@(PGTonePreset):@2};
|
||||||
|
PGInspectorState *a=State(values,nil);
|
||||||
|
NSData *archive=[NSKeyedArchiver archivedDataWithRootObject:a requiringSecureCoding:YES error:nil];
|
||||||
|
PGInspectorState *restored=[NSKeyedUnarchiver unarchivedObjectOfClass:PGInspectorState.class fromData:archive error:nil];
|
||||||
|
Check([restored isEqual:a] && Near([restored valueForParameter:PGFeatureProtectionAmount],.25) && Near([restored valueForParameter:PGNonSkinProtection],.6) && Near([restored valueForParameter:PGMicroDetail],.8),@"secure document archive retains all three independent numeric controls");
|
||||||
|
NSMutableDictionary *right=[values mutableCopy];right[@(PGFeatureProtectionAmount)]=@.75;right[@(PGNonSkinProtection)]=@.2;right[@(PGMicroDetail)]=@.4;
|
||||||
|
PGInspectorState *mid=(PGInspectorState *)[a interpolateBetween:State(right,nil) withWeight:.5];
|
||||||
|
Check(Near([mid valueForParameter:PGFeatureProtectionAmount],.5) && Near([mid valueForParameter:PGNonSkinProtection],.4) && Near([mid valueForParameter:PGMicroDetail],.6),@"new floating-point protection and micro-detail keyframes interpolate independently");
|
||||||
|
for(NSNumber *key in @[@(PGFeatureProtectionAmount),@(PGNonSkinProtection),@(PGMicroDetail)]) {
|
||||||
|
PGInspectorState *bypassed=State(values,[NSSet setWithObject:key]);
|
||||||
|
PGBeautySettings s=PGSettingsWithInspectorState(PGBeautyDefaults(),bypassed);
|
||||||
|
Check(Near(s.featureProtectionAmount,key.unsignedIntValue==PGFeatureProtectionAmount?0:.25) && Near(s.nonSkinProtection,key.unsignedIntValue==PGNonSkinProtection?0:.6) && Near(s.microDetail,key.unsignedIntValue==PGMicroDetail?0:.8),@"each new bypass affects only its own render setting");
|
||||||
|
Check(s.featureProtection==(s.featureProtectionAmount>0) && Near(s.skinHue,12) && Near(s.skinBrightness,.3) && s.skinTonePreset==2 && Near(s.highPassDetail,.4) && Near(s.sharpness,.5),@"protection bypass preserves color and legacy detail settings and derives the old feature bool");
|
||||||
|
}
|
||||||
|
NSMutableDictionary *off=[values mutableCopy];off[@(PGSmoothingEnabled)]=@0;
|
||||||
|
PGBeautySettings s=PGSettingsWithInspectorState(PGBeautyDefaults(),State(off,nil));
|
||||||
|
Check(s.amount==0 && s.midtoneDetail==0 && s.highPassDetail==0 && s.microDetail==0 && Near(s.skinHue,12) && Near(s.skinBrightness,.3),@"smoothing master bypass also disables micro detail while preserving color adjustments");
|
||||||
|
PGInspectorState *untouched=[PGInspectorState stateWithValues:@{@(PGFeatureProtect):@NO} disabledParameters:[NSSet set] hasEdits:NO];
|
||||||
|
PGBeautySettings original=PGBeautyDefaults();original.featureProtectionAmount=.8;original.nonSkinProtection=.2;original.microDetail=.7;
|
||||||
|
PGBeautySettings result=PGSettingsWithInspectorState(original,untouched);
|
||||||
|
Check(memcmp(&original,&result,sizeof(result))==0,@"an untouched migrated inspector still leaves existing document settings unchanged");
|
||||||
|
}
|
||||||
static void ModelTests(void) {
|
static void ModelTests(void) {
|
||||||
PGInspectorState *plain=[PGInspectorState stateWithValues:@{} disabledParameters:[NSSet set] hasEdits:NO];
|
PGInspectorState *plain=[PGInspectorState stateWithValues:@{} disabledParameters:[NSSet set] hasEdits:NO];
|
||||||
PGBeautySettings original=PGBeautyDefaults();original.midtoneDetail=.5;original.faceChin=-.3;
|
PGBeautySettings original=PGBeautyDefaults();original.midtoneDetail=.5;original.faceChin=-.3;
|
||||||
@@ -191,13 +230,51 @@ static void ViewInteractionTests(void) {
|
|||||||
Fire(number);
|
Fire(number);
|
||||||
Check(number.currentEditor==nil && Near([(PGInspectorState *)api.values[@(PGInspectorPanel)] valueForParameter:PGChin],.25),@"Return commits once and releases the actual AppKit editor before host commands");
|
Check(number.currentEditor==nil && Near([(PGInspectorState *)api.values[@(PGInspectorPanel)] valueForParameter:PGChin],.25),@"Return commits once and releases the actual AppKit editor before host commands");
|
||||||
[window close];
|
[window close];
|
||||||
NSButton *preview=(NSButton *)FindControl(view,NSButton.class,@"人脸框预览");preview.state=NSControlStateValueOn;Fire(preview);s=api.values[@(PGInspectorPanel)];Check([s valueForParameter:PGTrackBoxes]==1,@"preview switch events change the automatic face box setting");
|
NSButton *preview=(NSButton *)FindControl(view,NSButton.class,@"检测框预览");preview.state=NSControlStateValueOn;Fire(preview);s=api.values[@(PGInspectorPanel)];Check([s valueForParameter:PGTrackBoxes]==1,@"preview switch events change the automatic face box setting");
|
||||||
for(NSNumber *width in @[@280,@320,@380,@520]) {
|
for(NSNumber *width in @[@280,@320,@380,@520]) {
|
||||||
[view setFrameSize:NSMakeSize(width.doubleValue,20)];[view layoutSubtreeIfNeeded];
|
[view setFrameSize:NSMakeSize(width.doubleValue,20)];[view layoutSubtreeIfNeeded];
|
||||||
Check(view.frame.size.height>500 && Near(view.frame.size.height,view.fittingSize.height) && Near(view.frame.size.height,view.intrinsicContentSize.height),@"width changes produce a complete consistent intrinsic/fitting height");
|
Check(view.frame.size.height>500 && Near(view.frame.size.height,view.fittingSize.height) && Near(view.frame.size.height,view.intrinsicContentSize.height),@"width changes produce a complete consistent intrinsic/fitting height");
|
||||||
for(NSView *card in view.subviews)if(!card.hidden)Check(NSMaxY(card.frame)<=NSHeight(view.bounds)+1 && NSMaxX(card.frame)<=NSWidth(view.bounds)+1,@"every visible card and footer remains inside the panel at all inspector widths");
|
for(NSView *card in view.subviews)if(!card.hidden)Check(NSMaxY(card.frame)<=NSHeight(view.bounds)+1 && NSMaxX(card.frame)<=NSWidth(view.bounds)+1,@"every visible card and footer remains inside the panel at all inspector widths");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
static void BeautyControlInteractionTests(void) {
|
||||||
|
PGInspectorTestAPI *api=[PGInspectorTestAPI new];beatygrilPlugIn *plugin=[[beatygrilPlugIn alloc]initWithAPIManager:api];[plugin addParametersWithError:nil];
|
||||||
|
api.values[@(PGFeatureProtectionAmount)]=@.37;api.values[@(PGHighPass)]=@.63;api.values[@(PGHue)]=@12;api.values[@(PGBrightness)]=@.3;
|
||||||
|
PGInspectorView *view=(PGInspectorView *)[plugin createViewForParameterID:PGInspectorPanel];[view refreshFromHost];
|
||||||
|
Check(Near([view.controller.state valueForParameter:PGFeatureProtectionAmount],.37) && !view.controller.state.hasEdits,@"an untouched inspector preserves native fractional protection when the legacy boolean is enabled");
|
||||||
|
[view.controller setValue:.35 forParameter:PGBrightness];
|
||||||
|
Check(Near([(PGInspectorState *)api.values[@(PGInspectorPanel)] valueForParameter:PGFeatureProtectionAmount],.37) && Near([api.values[@(PGFeatureProtectionAmount)]doubleValue],.37),@"first unrelated edit captures native fractional protection without replacing it with the legacy true value");
|
||||||
|
api.values[@(PGInspectorPanel)]=[PGInspectorState stateWithValues:@{} disabledParameters:[NSSet set] hasEdits:NO];
|
||||||
|
api.values[@(PGFeatureProtect)]=@NO;[view hostParameterChanged];
|
||||||
|
Check([view.controller.state valueForParameter:PGFeatureProtectionAmount]==0 && !view.controller.state.hasEdits,@"untouched inspector imports the old host five-feature boolean into the new numeric control");
|
||||||
|
[view.controller setValue:.35 forParameter:PGBrightness];
|
||||||
|
Check(![api.values[@(PGFeatureProtect)]boolValue] && Near([view.controller.state valueForParameter:PGFeatureProtectionAmount],0),@"first unrelated edit preserves disabled legacy feature protection");
|
||||||
|
NSArray *ids=@[@(PGFeatureProtectionAmount),@(PGNonSkinProtection),@(PGMicroDetail)],*names=@[@"五官保护",@"非肤色区保护",@"微细锐化"];
|
||||||
|
for(NSUInteger i=0;i<ids.count;i++) {
|
||||||
|
UInt32 key=[ids[i]unsignedIntValue];NSString *name=names[i];
|
||||||
|
NSSlider *slider=(NSSlider *)FindControl(view,NSSlider.class,name);
|
||||||
|
NSTextField *number=(NSTextField *)FindControl(view,NSTextField.class,[name stringByAppendingString:@"数值"]);
|
||||||
|
Check(slider && number && slider.enabled && number.enabled,@"each new control exposes an enabled native slider and numeric field");
|
||||||
|
number.stringValue=@"27%";Fire(number);
|
||||||
|
Check(Near([view.controller.state valueForParameter:key],.27) && [number.stringValue isEqual:@"27%"],@"new numeric controls accept direct percentages without inverse interpretation");
|
||||||
|
slider.doubleValue=.42;Fire(slider);
|
||||||
|
Check(Near([view.controller.state valueForParameter:key],.42) && [number.stringValue isEqual:@"42%"],@"actual new slider events publish their direct amount and update the percentage display");
|
||||||
|
NSButton *eye=(NSButton *)FindControl(view,NSButton.class,[@"启用 / 停用 · "stringByAppendingString:name]);Fire(eye);
|
||||||
|
Check(![view.controller.state isParameterEnabled:key] && Near([view.controller.state valueForParameter:key],.42) && !slider.enabled,@"new individual bypass preserves its value and disables its native controls");
|
||||||
|
if(key==PGFeatureProtectionAmount)Check(![api.values[@(PGFeatureProtect)]boolValue],@"bypassed numeric feature protection mirrors false into the legacy hidden boolean");
|
||||||
|
NSButton *reset=(NSButton *)FindControl(view,NSButton.class,[@"重置 · "stringByAppendingString:name]);Fire(reset);
|
||||||
|
Check([view.controller.state isParameterEnabled:key] && [view.controller.state valueForParameter:key]==(key==PGMicroDetail?0:1) && slider.enabled,@"new individual reset restores the proper default and clears bypass");
|
||||||
|
}
|
||||||
|
Check([api.values[@(PGFeatureProtect)]boolValue] && Near([view.controller.state valueForParameter:PGHighPass],.63) && Near([view.controller.state valueForParameter:PGHue],12) && Near([view.controller.state valueForParameter:PGBrightness],.35),@"new resets restore the legacy feature bool without modifying global smoothing, hue or brightness");
|
||||||
|
NSButton *multi=(NSButton *)FindControl(view,NSButton.class,@"多人模式");Check([multi.toolTip containsString:@"易抖动"],@"multi-face mode exposes its instability notice as a tooltip");
|
||||||
|
[view.controller setValue:.6 forParameter:PGMicroDetail];
|
||||||
|
NSButton *master=(NSButton *)FindControl(view,NSButton.class,@"磨皮");master.state=NSControlStateValueOff;Fire(master);
|
||||||
|
PGBeautySettings s=PGSettingsWithInspectorState(PGBeautyDefaults(),view.controller.state);
|
||||||
|
Check(s.microDetail==0 && s.midtoneDetail==0 && s.highPassDetail==0 && Near(s.skinHue,12) && Near(s.skinBrightness,.35),@"actual smoothing master event disables new detail processing independently from whitening");
|
||||||
|
master.state=NSControlStateValueOn;Fire(master);
|
||||||
|
Check(Near(PGSettingsWithInspectorState(PGBeautyDefaults(),view.controller.state).microDetail,.6),@"reenabling the master restores the stored micro-detail amount");
|
||||||
|
Check(api.actionDepth==0 && api.actionStarts==api.actionEnds && api.accessViolations==0,@"all new numeric, bypass, reset and master interactions use balanced host actions");
|
||||||
|
}
|
||||||
static void HostHandshakeTests(void) {
|
static void HostHandshakeTests(void) {
|
||||||
PGInspectorTestAPI *api=[PGInspectorTestAPI new];beatygrilPlugIn *plugin=[[beatygrilPlugIn alloc]initWithAPIManager:api];[plugin addParametersWithError:nil];
|
PGInspectorTestAPI *api=[PGInspectorTestAPI new];beatygrilPlugIn *plugin=[[beatygrilPlugIn alloc]initWithAPIManager:api];[plugin addParametersWithError:nil];
|
||||||
api.values[@(PGMidtone)]=@.5;
|
api.values[@(PGMidtone)]=@.5;
|
||||||
@@ -219,24 +296,45 @@ static void ContinuousPreviewTests(void) {
|
|||||||
PGInspectorTestAPI *api=[PGInspectorTestAPI new];beatygrilPlugIn *plugin=[[beatygrilPlugIn alloc]initWithAPIManager:api];[plugin addParametersWithError:nil];
|
PGInspectorTestAPI *api=[PGInspectorTestAPI new];beatygrilPlugIn *plugin=[[beatygrilPlugIn alloc]initWithAPIManager:api];[plugin addParametersWithError:nil];
|
||||||
api.parameterAPIsRequireAction=YES;api.actionRequiresView=YES;
|
api.parameterAPIsRequireAction=YES;api.actionRequiresView=YES;
|
||||||
PGInspectorView *view=(PGInspectorView *)[plugin createViewForParameterID:PGInspectorPanel];[view refreshFromHost];
|
PGInspectorView *view=(PGInspectorView *)[plugin createViewForParameterID:PGInspectorPanel];[view refreshFromHost];
|
||||||
NSSlider *slider=(NSSlider *)FindControl(view,NSSlider.class,@"短脸");
|
NSArray *names=@[@"短脸",@"五官保护",@"非肤色区保护",@"微细锐化"],*ids=@[@(PGShort),@(PGFeatureProtectionAmount),@(PGNonSkinProtection),@(PGMicroDetail)];
|
||||||
void (^began)(void)=[slider valueForKey:@"interactionBegan"], (^ended)(void)=[slider valueForKey:@"interactionEnded"];
|
for(NSUInteger i=0;i<names.count;i++) {
|
||||||
Check(slider.continuous && began && ended,@"native slider emits continuous events and exposes balanced gesture callbacks");
|
UInt32 key=[ids[i]unsignedIntValue];NSSlider *slider=(NSSlider *)FindControl(view,NSSlider.class,names[i]);
|
||||||
began();
|
void (^began)(void)=[slider valueForKey:@"interactionBegan"], (^ended)(void)=[slider valueForKey:@"interactionEnded"];
|
||||||
for(NSNumber *value in @[@.2,@.5,@1]) {
|
Check(slider.continuous && began && ended,@"native slider emits continuous events and exposes balanced gesture callbacks");
|
||||||
NSUInteger publications=api.previewPublications;slider.doubleValue=value.doubleValue;Fire(slider);
|
began();
|
||||||
PGInspectorState *visible=api.previewValues[@(PGInspectorPanel)];
|
for(NSNumber *value in @[@.2,@.5,@1]) {
|
||||||
Check(api.actionDepth==0 && api.previewPublications==publications+1 && Near([visible valueForParameter:PGShort],value.doubleValue),@"every drag step publishes a completed host snapshot before mouse-up");
|
NSUInteger publications=api.previewPublications;slider.doubleValue=value.doubleValue;Fire(slider);
|
||||||
Check(visible && Near(PGSettingsWithInspectorState(PGBeautyDefaults(),visible).faceShort,value.doubleValue),@"published frame settings contain each intermediate short-face value while gesture is still open");
|
PGInspectorState *visible=api.previewValues[@(PGInspectorPanel)];
|
||||||
|
Check(api.actionDepth==0 && api.previewPublications==publications+1 && Near([visible valueForParameter:key],value.doubleValue),@"every old and new slider step publishes a completed host snapshot before mouse-up");
|
||||||
|
PGBeautySettings s=PGSettingsWithInspectorState(PGBeautyDefaults(),visible);
|
||||||
|
double actual=key==PGShort?s.faceShort:key==PGFeatureProtectionAmount?s.featureProtectionAmount:key==PGNonSkinProtection?s.nonSkinProtection:s.microDetail;
|
||||||
|
Check(visible && Near(actual,value.doubleValue),@"published frame settings contain each intermediate new protection/detail value while gesture is still open");
|
||||||
|
}
|
||||||
|
NSUInteger publications=api.previewPublications;Fire(slider);
|
||||||
|
Check(api.previewPublications==publications,@"unchanged drag events do not enqueue redundant previews");
|
||||||
|
ended();
|
||||||
}
|
}
|
||||||
NSUInteger publications=api.previewPublications;Fire(slider);
|
|
||||||
Check(api.previewPublications==publications,@"unchanged drag events do not enqueue redundant previews");
|
|
||||||
ended();
|
|
||||||
Check(api.actionDepth==0 && api.actionStarts==api.actionEnds && api.accessViolations==0 && api.invalidActionSenders==0,@"step-wise publication preserves action-scoped APIs and the actual view sender");
|
Check(api.actionDepth==0 && api.actionStarts==api.actionEnds && api.accessViolations==0 && api.invalidActionSenders==0,@"step-wise publication preserves action-scoped APIs and the actual view sender");
|
||||||
}
|
}
|
||||||
|
static void OptionalInspectorPreview(void) {
|
||||||
|
NSString *path=NSProcessInfo.processInfo.environment[@"PAINTFCP_INSPECTOR_PNG"];
|
||||||
|
if(!path.length)return;
|
||||||
|
PGHostAPI *api=[PGHostAPI new];beatygrilPlugIn *plugin=[[beatygrilPlugIn alloc]initWithAPIManager:api];[plugin addParametersWithError:nil];
|
||||||
|
api.values[@(PGMidtone)]=@.4;
|
||||||
|
PGInspectorView *view=(PGInspectorView *)[plugin createViewForParameterID:PGInspectorPanel];[view refreshFromHost];
|
||||||
|
[view setFrameSize:NSMakeSize(380,20)];
|
||||||
|
NSWindow *window=[[NSWindow alloc]initWithContentRect:view.bounds styleMask:NSWindowStyleMaskBorderless backing:NSBackingStoreBuffered defer:NO];
|
||||||
|
window.releasedWhenClosed=NO;window.appearance=[NSAppearance appearanceNamed:NSAppearanceNameDarkAqua];window.contentView=view;
|
||||||
|
[window setContentSize:view.frame.size];[view layoutSubtreeIfNeeded];[window displayIfNeeded];
|
||||||
|
NSBitmapImageRep *bitmap=[view bitmapImageRepForCachingDisplayInRect:view.bounds];
|
||||||
|
[view cacheDisplayInRect:view.bounds toBitmapImageRep:bitmap];
|
||||||
|
NSData *png=[bitmap representationUsingType:NSBitmapImageFileTypePNG properties:@{}];
|
||||||
|
Check(png.length>0 && [png writeToFile:path atomically:YES],@"optional offscreen inspector PNG is written to the requested review path");
|
||||||
|
[window close];
|
||||||
|
}
|
||||||
int main(int argc,const char **argv) {@autoreleasepool {
|
int main(int argc,const char **argv) {@autoreleasepool {
|
||||||
[NSApplication sharedApplication];[NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited];
|
[NSApplication sharedApplication];[NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited];
|
||||||
RegistrationTests();ModelTests();ControllerTests();ViewInteractionTests();HostHandshakeTests();ContinuousPreviewTests();
|
RegistrationTests();ModelTests();BeautyControlModelTests();ControllerTests();ViewInteractionTests();BeautyControlInteractionTests();HostHandshakeTests();ContinuousPreviewTests();OptionalInspectorPreview();
|
||||||
if(argc>2) {
|
if(argc>2) {
|
||||||
NSData *inner=[NSData dataWithContentsOfFile:@(argv[1])],*outer=[NSData dataWithContentsOfFile:@(argv[2])];NSError *error=nil;
|
NSData *inner=[NSData dataWithContentsOfFile:@(argv[1])],*outer=[NSData dataWithContentsOfFile:@(argv[2])];NSError *error=nil;
|
||||||
NSKeyedUnarchiver *reader=[[NSKeyedUnarchiver alloc]initForReadingFromData:inner error:&error];reader.requiresSecureCoding=YES;
|
NSKeyedUnarchiver *reader=[[NSKeyedUnarchiver alloc]initForReadingFromData:inner error:&error];reader.requiresSecureCoding=YES;
|
||||||
|
|||||||
+42
-7
@@ -28,6 +28,7 @@ static NSDictionary *Geometry(double yaw,double roll) {
|
|||||||
p[46]=@[@.5,@.68];p[47]=@[@.5,@.60];p[48]=@[@.5,@.52];p[49]=@[@.5,@.45];p[52]=@[@.5,@.42];
|
p[46]=@[@.5,@.68];p[47]=@[@.5,@.60];p[48]=@[@.5,@.52];p[49]=@[@.5,@.45];p[52]=@[@.5,@.42];
|
||||||
p[29]=@[@.5,@.39];
|
p[29]=@[@.5,@.39];
|
||||||
p[36]=@[@.54,@.36];p[37]=@[@.5,@.35];p[38]=@[@.46,@.36];
|
p[36]=@[@.54,@.36];p[37]=@[@.5,@.35];p[38]=@[@.46,@.36];
|
||||||
|
p[26]=@[@.42,@.40];p[34]=@[@.58,@.40];p[35]=@[@.57,@.36];p[39]=@[@.43,@.36];
|
||||||
for(NSUInteger i=0;i<17;i++) {
|
for(NSUInteger i=0;i<17;i++) {
|
||||||
double t=M_PI+M_PI*i/16;
|
double t=M_PI+M_PI*i/16;
|
||||||
p[59+i]=@[@(.5+.29*cos(t)),@(.65+.43*sin(t))];
|
p[59+i]=@[@(.5+.29*cos(t)),@(.65+.43*sin(t))];
|
||||||
@@ -79,7 +80,7 @@ static CGPoint ChinOracle(CGPoint pixel,NSDictionary *g,CGRect e,double thin,dou
|
|||||||
return CGPointMake(q.x,1-q.y);
|
return CGPointMake(q.x,1-q.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Independent double-precision reconstruction of AIR's jaw and short-face
|
// Independent double-precision reconstruction of AIR's V, jaw and short-face
|
||||||
// branches. This does not call or inspect the production kernel/parameter table.
|
// branches. This does not call or inspect the production kernel/parameter table.
|
||||||
// Jaw's support uses the adjacent contour edge, not the much longer lip vector.
|
// Jaw's support uses the adjacent contour edge, not the much longer lip vector.
|
||||||
// Short-face uses a filled lower-contour polygon and its distance envelope.
|
// Short-face uses a filled lower-contour polygon and its distance envelope.
|
||||||
@@ -108,6 +109,15 @@ static double ReferencePolygonWeight(CGPoint q,CGPoint p[76]) {
|
|||||||
static CGPoint JawShortOracle(CGPoint pixel,NSDictionary *g,CGRect e,PGBeautySettings s) {
|
static CGPoint JawShortOracle(CGPoint pixel,NSDictionary *g,CGRect e,PGBeautySettings s) {
|
||||||
CGPoint p[76];for(NSUInteger i=0;i<76;i++)p[i]=ReadPoint(g[@"points76"][i]);
|
CGPoint p[76];for(NSUInteger i=0;i<76;i++)p[i]=ReadPoint(g[@"points76"][i]);
|
||||||
double gain=ReferenceSizeGain(p,e);CGPoint q=Oracle(pixel,g,e,s.faceThin);q.y=1-q.y;
|
double gain=ReferenceSizeGain(p,e);CGPoint q=Oracle(pixel,g,e,s.faceThin);q.y=1-q.y;
|
||||||
|
// 2.4.0 consumes the CPU's .065 V ratio directly, without the old size gain.
|
||||||
|
const unsigned int vOrigins[]={69,65,70,64},vAnchors[]={39,35,26,34};
|
||||||
|
double vRatio=Clamp(s.faceV,0,1)*.065;
|
||||||
|
if(vRatio>1e-6)for(NSUInteger i=0;i<4;i++){
|
||||||
|
CGPoint a=ReferencePoint(p,vOrigins[i]),b=ReferencePoint(p,vAnchors[i]);
|
||||||
|
double radius=hypot(b.x-a.x,b.y-a.y);if(radius<=.0001)continue;
|
||||||
|
double influence=Clamp(1-hypot(q.x-a.x,q.y-a.y)/radius,0,1);
|
||||||
|
q.x=Clamp(q.x-(b.x-a.x)*vRatio*influence,0,1);q.y=Clamp(q.y-(b.y-a.y)*vRatio*influence,0,1);
|
||||||
|
}
|
||||||
const unsigned int origin[]={72,62,71,63,70,64},neighbour[]={71,63,70,64,71,63};
|
const unsigned int origin[]={72,62,71,63,70,64},neighbour[]={71,63,70,64,71,63};
|
||||||
double jaw=Clamp(s.faceJaw,0,1)*.05*gain;
|
double jaw=Clamp(s.faceJaw,0,1)*.05*gain;
|
||||||
if(fabs(jaw)>1e-6)for(NSUInteger i=0;i<6;i++){
|
if(fabs(jaw)>1e-6)for(NSUInteger i=0;i<6;i++){
|
||||||
@@ -251,6 +261,21 @@ static void JawShortAnatomicalChecks(CIContext *context,PGFaceProcessor *process
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
static void VFaceAnatomicalChecks(CIContext *context,PGFaceProcessor *processor,NSDictionary *geometry,CGRect extent) {
|
||||||
|
CIImage *ramp=CoordinateImage(extent);PGBeautySettings settings=PGBeautyDefaults();settings.faceV=1;
|
||||||
|
CIImage *output=[processor warpImage:ramp geometry:@[geometry] settings:settings extent:extent];
|
||||||
|
NSArray *points=geometry[@"points76"];double lowerMove=0;
|
||||||
|
for(NSNumber *index in @[@69,@65,@70,@64]) {
|
||||||
|
CGPoint motion=Displacement(context,ramp,output,ReadPoint(points[index.unsignedIntegerValue]));
|
||||||
|
lowerMove=fmax(lowerMove,hypot(motion.x,motion.y));
|
||||||
|
}
|
||||||
|
Check(lowerMove>.1,@"V-face moves the actual lower-jaw contour");
|
||||||
|
for(NSNumber *index in @[@6,@13]) {
|
||||||
|
CGPoint motion=Displacement(context,ramp,output,ReadPoint(points[index.unsignedIntegerValue]));
|
||||||
|
if(hypot(motion.x,motion.y)>=.002)fprintf(stderr,"V-face pupil %u moved %.6f pixels\n",index.unsignedIntValue,hypot(motion.x,motion.y));
|
||||||
|
Check(hypot(motion.x,motion.y)<.002,@"isolated V-face leaves the physical pupils unchanged");
|
||||||
|
}
|
||||||
|
}
|
||||||
static void JawShortSupportChecks(CIContext *context,PGFaceProcessor *processor) {
|
static void JawShortSupportChecks(CIContext *context,PGFaceProcessor *processor) {
|
||||||
NSDictionary *g=Geometry(0,0);CGPoint points[76];for(NSUInteger i=0;i<76;i++)points[i]=ReadPoint(g[@"points76"][i]);
|
NSDictionary *g=Geometry(0,0);CGPoint points[76];for(NSUInteger i=0;i<76;i++)points[i]=ReadPoint(g[@"points76"][i]);
|
||||||
CGPoint probe=CGPointMake((points[72].x+points[29].x)/2,(points[72].y+points[29].y)/2);
|
CGPoint probe=CGPointMake((points[72].x+points[29].x)/2,(points[72].y+points[29].y)/2);
|
||||||
@@ -267,16 +292,18 @@ static void JawShortSupportChecks(CIContext *context,PGFaceProcessor *processor)
|
|||||||
Check(ReferenceSizeGain(points,e)==2.25,@"small-face numerical case reaches the recovered gain cap");
|
Check(ReferenceSizeGain(points,e)==2.25,@"small-face numerical case reaches the recovered gain cap");
|
||||||
NumericLocalCase(context,processor,e,small,settings,@"small face jaw max gain");settings.faceJaw=0;settings.faceShort=1;
|
NumericLocalCase(context,processor,e,small,settings,@"small face jaw max gain");settings.faceJaw=0;settings.faceShort=1;
|
||||||
NumericLocalCase(context,processor,e,small,settings,@"small face short max gain");
|
NumericLocalCase(context,processor,e,small,settings,@"small face short max gain");
|
||||||
|
settings.faceShort=0;settings.faceV=1;
|
||||||
|
NumericLocalCase(context,processor,e,small,settings,@"small face V excludes legacy size gain");
|
||||||
for(NSUInteger i=0;i<76;i++)points[i]=ReadPoint(g[@"points76"][i]);Check(ReferenceSizeGain(points,CGRectMake(0,0,1600,1600))==1,@"large-face numerical case reaches gain floor");
|
for(NSUInteger i=0;i<76;i++)points[i]=ReadPoint(g[@"points76"][i]);Check(ReferenceSizeGain(points,CGRectMake(0,0,1600,1600))==1,@"large-face numerical case reaches gain floor");
|
||||||
// Four full process masks must use exactly the same new warp as the image.
|
// Four full process masks must use exactly the same new warp as the image.
|
||||||
[processor setTrackingData:Archive(g) forTarget:0];settings=PGBeautyDefaults();settings.trackingEnabled=1;
|
[processor setTrackingData:Archive(g) forTarget:0];settings=PGBeautyDefaults();settings.trackingEnabled=1;
|
||||||
PGFaceFrame *neutral=[processor processImage:ramp settings:settings time:kCMTimeZero error:nil];
|
PGFaceFrame *neutral=[processor processImage:ramp settings:settings time:kCMTimeZero error:nil];
|
||||||
for(NSUInteger mode=0;mode<3;mode++){
|
for(NSUInteger mode=0;mode<4;mode++){
|
||||||
PGBeautySettings shape=settings;shape.faceJaw=mode!=1?1:0;shape.faceShort=mode!=0?-.65:0;
|
PGBeautySettings shape=settings;shape.faceJaw=(mode==0||mode==2)?1:0;shape.faceShort=(mode==1||mode==2)?-.65:0;shape.faceV=mode==3?1:0;
|
||||||
PGFaceFrame *frame=[processor processImage:ramp settings:shape time:kCMTimeZero error:nil];
|
PGFaceFrame *frame=[processor processImage:ramp settings:shape time:kCMTimeZero error:nil];
|
||||||
Check([Pixels(context,frame.image)isEqualToData:Pixels(context,[processor warpImage:ramp geometry:@[g] settings:shape extent:e])],@"process image uses the new common jaw/short field");
|
Check([Pixels(context,frame.image)isEqualToData:Pixels(context,[processor warpImage:ramp geometry:@[g] settings:shape extent:e])],@"process image uses the common V/jaw/short field");
|
||||||
NSArray *before=@[neutral.skinMask,neutral.colorMask,neutral.teethMask,neutral.eyeMask],*after=@[frame.skinMask,frame.colorMask,frame.teethMask,frame.eyeMask];
|
NSArray *before=@[neutral.skinMask,neutral.colorMask,neutral.teethMask,neutral.eyeMask],*after=@[frame.skinMask,frame.colorMask,frame.teethMask,frame.eyeMask];
|
||||||
for(NSUInteger i=0;i<4;i++)Check([Pixels(context,after[i])isEqualToData:Pixels(context,[processor warpImage:before[i] geometry:@[g] settings:shape extent:e])],@"every image support mask shares the jaw/short coordinate field");
|
for(NSUInteger i=0;i<4;i++)Check([Pixels(context,after[i])isEqualToData:Pixels(context,[processor warpImage:before[i] geometry:@[g] settings:shape extent:e])],@"every image support mask shares the V/jaw/short coordinate field");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static void IndependentImageLandmarkCheck(NSString *path,NSDictionary *geometry) {
|
static void IndependentImageLandmarkCheck(NSString *path,NSDictionary *geometry) {
|
||||||
@@ -318,16 +345,21 @@ int main(int argc,const char *argv[]) {@autoreleasepool {
|
|||||||
NumericSettingsCase(c,p,extents[2],Geometry(-.6,-.2),1,-1,@"thin plus negative chin");
|
NumericSettingsCase(c,p,extents[2],Geometry(-.6,-.2),1,-1,@"thin plus negative chin");
|
||||||
NumericSettingsCase(c,p,extents[0],Geometry(0,0),0,.5,@"half positive chin");
|
NumericSettingsCase(c,p,extents[0],Geometry(0,0),0,.5,@"half positive chin");
|
||||||
NumericSettingsCase(c,p,extents[0],Geometry(0,0),0,-.5,@"half negative chin");
|
NumericSettingsCase(c,p,extents[0],Geometry(0,0),0,-.5,@"half negative chin");
|
||||||
for(NSUInteger e=0;e<4;e++)for(NSUInteger y=0;y<3;y++)for(NSUInteger r=0;r<3;r++)for(NSUInteger mode=0;mode<3;mode++){
|
for(NSUInteger e=0;e<4;e++)for(NSUInteger y=0;y<3;y++)for(NSUInteger r=0;r<3;r++)for(NSUInteger mode=0;mode<4;mode++){
|
||||||
PGBeautySettings local=PGBeautyDefaults();if(mode==0)local.faceJaw=1;else local.faceShort=mode==1?1:-1;
|
PGBeautySettings local=PGBeautyDefaults();if(mode==0)local.faceJaw=1;else if(mode==3)local.faceV=1;else local.faceShort=mode==1?1:-1;
|
||||||
double angle=y==0?0:(y==1?1.4:-1.4);
|
double angle=y==0?0:(y==1?1.4:-1.4);
|
||||||
NumericLocalCase(c,p,extents[e],Geometry(angle,roll[r]),local,
|
NumericLocalCase(c,p,extents[e],Geometry(angle,roll[r]),local,
|
||||||
[NSString stringWithFormat:@"local size%lu yaw%lu roll%lu mode%lu",(unsigned long)e,(unsigned long)y,(unsigned long)r,(unsigned long)mode]);
|
[NSString stringWithFormat:@"local size%lu yaw%lu roll%lu mode%lu",(unsigned long)e,(unsigned long)y,(unsigned long)r,(unsigned long)mode]);
|
||||||
|
if(mode==3)VFaceAnatomicalChecks(c,p,Geometry(angle,roll[r]),extents[e]);
|
||||||
}
|
}
|
||||||
PGBeautySettings combined=PGBeautyDefaults();combined.faceThin=.7;combined.faceJaw=.8;combined.faceChin=-.6;combined.faceShort=.75;
|
PGBeautySettings combined=PGBeautyDefaults();combined.faceThin=.7;combined.faceJaw=.8;combined.faceChin=-.6;combined.faceShort=.75;
|
||||||
NumericLocalCase(c,p,extents[2],Geometry(.8,.45),combined,@"ordered thin jaw chin short positive");
|
NumericLocalCase(c,p,extents[2],Geometry(.8,.45),combined,@"ordered thin jaw chin short positive");
|
||||||
combined.faceShort=-.75;combined.faceChin=.7;
|
combined.faceShort=-.75;combined.faceChin=.7;
|
||||||
NumericLocalCase(c,p,extents[1],Geometry(-.8,-.45),combined,@"ordered thin jaw chin short negative");
|
NumericLocalCase(c,p,extents[1],Geometry(-.8,-.45),combined,@"ordered thin jaw chin short negative");
|
||||||
|
combined.faceV=.85;
|
||||||
|
NumericLocalCase(c,p,extents[3],Geometry(.6,.2),combined,@"thin V jaw chin short combined sequential order");
|
||||||
|
PGBeautySettings vHalf=PGBeautyDefaults();vHalf.faceV=.5;
|
||||||
|
NumericLocalCase(c,p,extents[2],Geometry(-.6,-.4),vHalf,@"half V strength tilted portrait");
|
||||||
combined=PGBeautyDefaults();combined.faceJaw=.5;combined.faceShort=-.5;
|
combined=PGBeautyDefaults();combined.faceJaw=.5;combined.faceShort=-.5;
|
||||||
NumericLocalCase(c,p,extents[1],Geometry(0,0),combined,@"half signed short plus jaw");
|
NumericLocalCase(c,p,extents[1],Geometry(0,0),combined,@"half signed short plus jaw");
|
||||||
JawShortSupportChecks(c,p);
|
JawShortSupportChecks(c,p);
|
||||||
@@ -361,6 +393,8 @@ int main(int argc,const char *argv[]) {@autoreleasepool {
|
|||||||
Check([Pixels(c,[p processImage:image settings:shortOnly time:kCMTimeZero error:&error].image) isEqualToData:source],@"legacy/malformed indexed points skip recovered short-face field exactly");
|
Check([Pixels(c,[p processImage:image settings:shortOnly time:kCMTimeZero error:&error].image) isEqualToData:source],@"legacy/malformed indexed points skip recovered short-face field exactly");
|
||||||
PGBeautySettings jawOnly=s;jawOnly.faceThin=0;jawOnly.faceJaw=1;
|
PGBeautySettings jawOnly=s;jawOnly.faceThin=0;jawOnly.faceJaw=1;
|
||||||
Check([Pixels(c,[p processImage:image settings:jawOnly time:kCMTimeZero error:&error].image) isEqualToData:source],@"legacy/malformed indexed points skip recovered jaw field exactly");
|
Check([Pixels(c,[p processImage:image settings:jawOnly time:kCMTimeZero error:&error].image) isEqualToData:source],@"legacy/malformed indexed points skip recovered jaw field exactly");
|
||||||
|
PGBeautySettings vOnly=s;vOnly.faceThin=0;vOnly.faceV=1;
|
||||||
|
Check([Pixels(c,[p processImage:image settings:vOnly time:kCMTimeZero error:&error].image) isEqualToData:source],@"legacy/malformed indexed points skip recovered V-face without a guessed face-box band");
|
||||||
PGBeautySettings other=s;other.faceNarrow=.5;
|
PGBeautySettings other=s;other.faceNarrow=.5;
|
||||||
Check(![Pixels(c,[p processImage:image settings:other time:kCMTimeZero error:&error].image) isEqualToData:source],@"other controls remain available in legacy archives");
|
Check(![Pixels(c,[p processImage:image settings:other time:kCMTimeZero error:&error].image) isEqualToData:source],@"other controls remain available in legacy archives");
|
||||||
}
|
}
|
||||||
@@ -385,6 +419,7 @@ int main(int argc,const char *argv[]) {@autoreleasepool {
|
|||||||
Check([Pixels(c,[copy processImage:portrait settings:rs time:kCMTimeZero error:&error].image) isEqualToData:Pixels(c,f.image)],@"real pose replays from persisted 76 points with identical pixels");
|
Check([Pixels(c,[copy processImage:portrait settings:rs time:kCMTimeZero error:&error].image) isEqualToData:Pixels(c,f.image)],@"real pose replays from persisted 76 points with identical pixels");
|
||||||
AnatomicalChecks(c,real,f.deformationGeometry.firstObject,portrait.extent);
|
AnatomicalChecks(c,real,f.deformationGeometry.firstObject,portrait.extent);
|
||||||
JawShortAnatomicalChecks(c,real,f.deformationGeometry.firstObject,portrait.extent);
|
JawShortAnatomicalChecks(c,real,f.deformationGeometry.firstObject,portrait.extent);
|
||||||
|
VFaceAnatomicalChecks(c,real,f.deformationGeometry.firstObject,portrait.extent);
|
||||||
IndependentImageLandmarkCheck(@(argv[index]),f.deformationGeometry.firstObject);
|
IndependentImageLandmarkCheck(@(argv[index]),f.deformationGeometry.firstObject);
|
||||||
}}
|
}}
|
||||||
printf("PASS: %lu slim alignment checks; maximum UV oracle error %.9g\n",(unsigned long)checks,maxOracleError);return 0;
|
printf("PASS: %lu slim alignment checks; maximum UV oracle error %.9g\n",(unsigned long)checks,maxOracleError);return 0;
|
||||||
|
|||||||
在新工单中引用
屏蔽一个用户