拖一个 .swf 就能跑:Ruffle 桌面端从文件落到渲染的完整链路
2026/9/9 13:37:19 网站建设 项目流程

拖一个 .swf 就能跑:Ruffle 桌面端从文件落到渲染的完整链路

【免费下载链接】ruffleA Flash Player emulator written in Rust项目地址: https://gitcode.com/GitHub_Trending/ru/ruffle

把鼠标指针按在任意一个.swf文件上,拖到 Ruffle 窗口里松手——这是 Flash 模拟器 Ruffle 桌面版最省事的一个动作。它把「用户手里一个路径」变成「窗口里正在播放的一段动画」,中间没有对话框、没有格式确认。这篇文章就盯住这条链路:事件从哪来,数据穿过哪几层,画面怎么落上去。

桌面端三块积木各自管什么

整个桌面壳子大致分三块。App/MainWindow负责接操作系统事件、维持一个 tokio 运行时;GuiController管 wgpu 渲染目标和 egui 画的那条菜单栏;PlayerController则罩住真正的Player,SWF 的解析、AVM 虚拟机、帧循环都躲在它后面。拖放这件事,前两块各出一只手:事件在MainWindow里被截获,创建动作交给GuiController,而「能不能播」的判定其实落在第三块的取流阶段。

从 DroppedFile 事件到 create_movie

winit 把操作系统层面的拖放压平成一个事件,没有进入/悬停的中间态,只有「文件已经落地」这一个瞬间:

WindowEvent::DroppedFile(file) => { if let Some(content_descriptor) = ContentDescriptor::new_local(&file, None) { self.gui.create_movie( &mut self.player, LaunchOptions::from(&self.preferences), content_descriptor, ); } }

这一段在 app.rs 的window_event里。file是一个PathBufContentDescriptor::new_local做的事很轻——用Url::from_file_path把本地路径包成file://URL,同时留一个空的root_content_path(content.rs)。注意这里没有任何扩展名检查,拖进来是什么就包什么,合法性判定被刻意推迟了。

create_movie在 controller.rs 里,先close_movie把旧播放器整个销毁,再MovieView::new造一个新的渲染目标,然后调player.create。也就是说,每次拖放都会把上一个播放的 SWF 从内存里连根拔掉,而不是叠加。

SWF 头如何反过来决定窗口尺寸

player.create在 player.rs 里走 builder 模式把渲染器、导航器、UI 后端全挂上,最后调fetch_root_movie并塞进去一个on_metadata回调。这个回调在 core 真正读到 SWF 文件头时触发,把HeaderExt(含舞台尺寸)通过RuffleEvent::OnMetadata抛回事件循环。

MainWindow::on_metadata收到它之后才做窗口布局:拿stage_size()算出目标逻辑尺寸,套上菜单栏高度,Size::clamp到屏幕上限,然后request_inner_size。如果 X11 上报的当前尺寸还和请求值对不上,状态机就停在WaitingForResize,等到下一次Resized事件确认到位才翻成Loaded。这个小小的三态枚举(Loading / WaitingForResize / Loaded)保证了 SWF 里那些能观察视口尺寸的代码不会在窗口还没定型时读到错的值。

两层画面怎么合成到一块屏幕

真正上屏时,一帧被拆成两段 draw。GuiController::render先从 wgpu 拿到 surface 纹理,开一个 render pass:先调movie_view.render,把 SWF 内容从它的离屏纹理贴到屏幕上;紧接着egui_renderer.render,把菜单栏、按钮那些 egui 三角形叠上去。SWF 自己渲染进的是MovieView持有的RenderTarget纹理,egui 走的是另一套 tessellate 管线,两者互不干扰,最后在同一个 pass 里前后合成。这就是为什么菜单可以被拖拽、缩放时独立重绘,而不会把下面的 SWF 画面也一起重算。

校验为什么不做在拖放入口

一个容易踩的坑是:在DroppedFile那里就检查.swf后缀、读 magic 字节,不合格直接弹错。Ruffle 没这么做。文件对话框那条路(picker.rs)确实按swf/spl/ruf分了过滤器,可拖放这条路完全不过滤,ContentDescriptor::new_local对任何路径都返回Some。真正的合法性检查下沉到了 core 取流时读文件头的那一步——magic 不对,fetch_root_movie自然失败,错误沿通知通道弹回 UI。把信任边界压到最深处的好处是:拖一个.txt进来不会 panic,只是播不起来;而且本地文件、bundle、远程 URL 三条入口共享同一套判定,不用在入口各写一份。

本地跑一条命令

cargo run -p ruffle-desktop -- /path/to/movie.swf,把文件当参数传进去,或直接启动后拖进去,效果一致。

拖放这条路目前只认单个本地文件,多文件拖入、bundle 目录拖入都还没接上——这块大概率是下一个要补的口子,值得留意。

【免费下载链接】ruffleA Flash Player emulator written in Rust项目地址: https://gitcode.com/GitHub_Trending/ru/ruffle

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询