跳到正文

Native sprite / animation / border-image 实现记录

历史验证记录:Native sprite / animation / border-image 实现记录。请结合日期、环境和证据限制阅读。

本页保留当时的验证结论,不代表当前完整平台能力。当前状态见项目进展,运行时说明见JavaScriptCore。

这轮将此前只有字段或部分绘制支持的能力接入 Rust 资源、投影和 GPU 链路。引擎/store 仍拥有人物、表情、动画和剧情状态;Native 只解析已挂载 QPK 的资源元数据并绘制投影。

已补齐的部分

  • Border image:QSS source/slice/width/repeat 进入真实九宫格绘制,支持 stretch、居中 repeat、边缘残片裁切、fill 和目的边框相互重叠时的等比压缩。slice 使用源图 texel,width 使用逻辑舞台单位;DPR 只缩放目的几何。切片采样限制在各自区域,修复 repeat 时相邻颜色渗入造成的接缝。
  • Sprite 资源:原生 product loop 从 mounted QPK 读取 version-1 manifest,按引擎 expression 解析图层,合并 atlas frame 覆盖字段并处理声明的 missing-asset fallback。支持默认 family manifest 和显式 JSON sprite 引用;已解析的 spriteBase/spriteLayers 优先。资源查找保留 package priority/provenance,不退到无关包。
  • Sprite 绘制:atlas 使用解码尺寸转换 texel UV;无效或尚无尺寸的 crop 不显示整张 atlas。父子 offset/scale/rotation 组合、局部 mask/blend 和父级整体透明度进入绘制链路。人物负 scale 按 Web 等比负缩放处理,即两轴反向;图层 scale 现也支持有界负值和零值;见下方追加的图层动画验证。
  • 引擎动画:修正 delay/fill 导致停止请求帧、playbackRate 与活动期不一致等问题。实时 app 使用 Rust 瞬时插值,固定时间 TS 序列化继续复用共享 render-core;没有活动 timeline 时保留引擎已结算的 dialogue/choices motion。
  • 可见动画目标:stage/camera 对场景子树变换;dialogue、choices panel、choice 和 UI overlay 的位置、scale、rotation、opacity 作为整组绘制,命中测试反变换,hover variant 保留组变换,UI 保留原有效叠层位置。rich-text document/block/span 的 typography 动画同时更新 revealed text 和 full-layout text。数值 font-weight 插值取原生整数字重。span target 使用共享格式 richTextSpan:dialogue:<id-or-index>,允许跨 block 同名目标。
  • 生命周期:sprite metadata cache 限制为 128 个查询、单 manifest 2 MiB、单表情 256 层;mount/version/hash 变化、显式 lifecycle tick 和 shutdown 清理缓存。静态缓存帧不额外查询 host。图片和 mask 继续进入既有 package 资源清理链路。

实际光栅验证

macOS Apple M4 / Metal 与 Chrome,1920×1080 逻辑舞台,同一 Quack QPK。features.mjs 使用显式尺寸的 sprite/atlas fixture 和 Web spriteLayerStyle 变换/mask 样式;它不覆盖混合自然尺寸的完整 Web sprite DOM 布局。

用例RGB MAE(0..255)最大通道差超过 32 的像素占比
border stretch/repeat/fill/hollow/重叠宽度0.6350.0614%
border,1920×1200 留黑边0.6350.0614%
manifest + atlas + 父子旋转/负缩放0.8080.3818%
sprite mask/blend + 父子变换0.2660.1442%
stage/camera + sprite 组合0.5720.1263%

误差按两张图片已绘制区域的并集归一化,黑边不稀释误差;门槛保持 MAE ≤ 3、超 32 比例 ≤ 1.5%。首次对照确实检出了切片接缝并据此修复,未放宽门槛。另有 11/11 resolved sprite 回归通过,包括透明度、presence、隐藏层、局部和人物层级、mask/blend、留黑边;该组最坏 MAE 0.111,超 16 像素比例为 0。

Shell
node scripts/native-render-audit/features.mjsnode scripts/native-render-audit/sprite.mjs

报告及成对 PNG:packages/native/target/render-audit/{features,sprite}/{measurements.json,review.html}。允许用 QUA_NATIVE_AUDIT_APP + --skip-build 指定已构建二进制;features 报告记录 SHA-256。截图验证包含解码上传和退出清理,无资源错误。这些是指定夹具的覆盖结果,不是整个 renderer 的视觉评分。

验证与后续边界

Rust renderer 871 项、完整 native-window/QuickJS/audio app 282 项 + 14 CLI 项通过;engine-native 完整套件 108 项、typecheck/build 通过;native-ui-compiler 225 项通过。完整 demo E2E 已验证剧情、分支、settings/gallery、原生音频退出清理和实际 Metal PNG 回读,最终完整流程日志为 packages/native/target/parity-demo-e2e-final.log;详见 执行优化记录。窗口仍报告 occluded,不宣称已验证可见 OS 窗口呈现。

仍需继续补齐:

  • CSS border-image percentage slice、gradient、outset、round/space;超过 4096 个 tile 的病态输入目前退回 stretch。
  • 自动 UI skin manifest 绑定;混合自然尺寸/trimmed atlas frame 的 sprite canvas 排版;spriteLayer 最终状态 commit adapter(下述瞬时绘制已支持)。
  • 任意 rich block/span 仿射变换和完整 typography/bidi/font-fallback/ruby 契约。
  • 通用 animation-plugin 的 audioBus/audioTrack target(原生 EQ/automation 属于独立音频投影链路);完整分层背景根变换和更广的 composition 属性组合。
  • MP4/WebM 等完整视频解码及可见 OS 窗口呈现验证。

上述未完成能力不能由“目标匹配到 JSON 字段”或“存在类型声明”推断为已完成。

追加:Sprite 图层动画已接通

本批补齐真实引擎 timeline → Rust 帧时钟 → QPK manifest → GPU 绘制链路。图层资源在 product loop 才解析,因此时钟先采样 transient spriteLayerAnimationValues,绘制阶段再应用到已解析图层。未增加时钟、事件总线或权威状态;测试验证动画不修改原始 manifest,也不额外读取已缓存的 QPK。

支持 offsetX、offsetY、scale、rotation、opacity、整数 zIndex、visible、blendMode。选择器分组依次应用 spriteLayer:<id>:<kind>:<index>、spriteLayer:<id>:<kind>、spriteLayer:<id>:<index>,后组覆盖前组;base 索引为 0,expression 从 1 起,atlas 保留 base/expression kind。支持含冒号的角色 id,避免前缀碰撞。单角色最多 1024 个采样值,忽略不安全的数值和不支持的属性。移除 timeline 清除派生值,暂停不会积累位移。

图层零缩放隐藏该层,有界负缩放按两轴反向处理,image 和 mask 一同旋转;base 层级不再被强制归零。固定时间 TS serializer 复用 render-core;帧级 projectAnimations:false 现在正确转发。精简帧缺少 plugins 时,动画序列化也不会访问不存在的 audio 字段。引擎 timeline 的 ease-in/out/in-out 已修正为共享 render-core 的二次曲线,CSS interaction 曲线保持 CSS 语义。

commit: 'none' 是当前图层效果的使用方式。Web 与 Native 均没有官方 spriteLayer 最终状态 adapter:默认非严格模式会警告但仍发布 timeline,strictAdapters:true 要求应用提供 adapter。commit:none 加 forwards/both fill 可由引擎保留结束投影。资源切换 asset/mask/frame 不属于本批样式动画支持,不能宣称默认 commit:final 或只写 decorator 就能持久化最终图层状态。

最终构建的真实截图

使用实际 Web updateSpriteLayerAnimations / spriteLayerStyle,Native 使用真实 Rust 时钟并由 app 解析同一 Quack QPK。固定 240×240 图层画布、1920×1080 逻辑舞台,父级缩放/旋转/透明度共同参与。测试 epoch 保证角色入场已结束,两端共享确定的 timeline 局部时间。12/12 通过,门槛未放宽。Native 回读保留 alpha,Web 截图已合成到黑色;比较时两者均先合成到相同黑色背景,报告图片也使用黑色底。

用例RGB MAE(0..255)最大通道差超过 32 的像素占比
原始组合0.0910.0049%
表情组位移 + 单层透明度0.1430.0091%
选择器优先级0.1810.1659%
base 缩放为零0.2450.4162%
负缩放 + mask + 旋转0.1120.0050%
base 动画改变层级0.1820.0000%
离散隐藏 + blend0.0050.0000%
delay / fill:none0.0910.0049%
0.5 倍速 + alternate 循环0.3490.5888%
暂停后固定采样0.1000.0049%
引擎 ease-in0.3130.3597%
引擎 ease-out + 留黑边0.2100.3278%

全部用例纹理上传、字体及 shutdown cleanup 错误为零,上传纹理全部释放。浏览器 152.0.7977.76;app SHA-256 474ffb381e05a2b0e4953c9c99e35cfea66fff72093b1ff9162d07b53164c70d;时钟 adapter SHA-256 89e55d5e1046d1f538f44482b78cbd179c38d4159a4eaf2e5a217ef1aa18ea82。报告记录生成时的工作树状态和 revision。

Shell
node scripts/native-render-audit/sprite-animation.mjs

报告与成对 PNG 位于 packages/native/target/render-audit/sprite-animation/。可用 QUA_NATIVE_AUDIT_APP、QUA_NATIVE_AUDIT_CLOCK 与 --skip-build 指定两个已构建的精确二进制。外置卷启动受阻时,将相同二进制复制到 /tmp 后运行;不关闭签名校验。

本批验证

  • Rust renderer 876 项;完整 native-window/QuickJS/audio app 283 + 14 CLI 项通过。
  • engine-native 完整 110 项通过,含真实 Rust/rquickjs 调用 character/animation helpers、采样图层轨道、实际 render graph 与结算几何对照、package unload;typecheck/build 通过。最后补充的前缀隔离用例另行复核通过。
  • 完整 pnpm native:e2e 通过:63 条 dialogue projection、stealth 分支、settings/gallery/返回 title;199 commands / 4 passes,1920×1080 Metal PNG 回读有效。纹理/字体/清理错误为零,退出后音频轨道为零。日志:packages/native/target/sprite-animation-demo-e2e.log。
  • OS 窗口仍报告 OccludedAfterRetry / presented:false;上述 GPU 证据来自真实 offscreen Metal 回读,不代表可见 OS 窗口已验证。

混合自然尺寸/trimmed atlas 排版、自动 UI skin、任意富文本仿射变换、通用音频 timeline、完整视频解码等仍按上方边界继续推进。

在 GitHub 查看 / 改进本页来源