From cd7f841e38d59292458f2bfe3b8c24fc54b201ab Mon Sep 17 00:00:00 2001 From: ww-rm Date: Thu, 24 Jul 2025 17:55:10 +0800 Subject: [PATCH 1/8] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=E6=96=87=E6=9C=AC=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SpineViewer/Resources/Strings/en.xaml | 2 +- SpineViewer/Resources/Strings/ja.xaml | 2 +- SpineViewer/Resources/Strings/zh.xaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/SpineViewer/Resources/Strings/en.xaml b/SpineViewer/Resources/Strings/en.xaml index 34e67ef..6d869ae 100644 --- a/SpineViewer/Resources/Strings/en.xaml +++ b/SpineViewer/Resources/Strings/en.xaml @@ -184,7 +184,7 @@ Codec FFmpeg codec (equivalent to "-c:v"), e.g. "libx264", "libx265" Pixel Format - FFmpeg pixel format (equivalent to "-pix_fmt"), e.g. "yuv420", "yuv444" + FFmpeg pixel format (equivalent to "-pix_fmt"), e.g. "yuv420p", "yuv444p" Bitrate FFmpeg bitrate (equivalent to "-b:v"), e.g. "6K", "2M" Filter diff --git a/SpineViewer/Resources/Strings/ja.xaml b/SpineViewer/Resources/Strings/ja.xaml index f3c1b3d..e41ab1a 100644 --- a/SpineViewer/Resources/Strings/ja.xaml +++ b/SpineViewer/Resources/Strings/ja.xaml @@ -184,7 +184,7 @@ コーデック FFmpegコーデック。パラメーター“-c:v”に相当します。例: “libx264”、“libx265” ピクセルフォーマット - FFmpegピクセルフォーマット。パラメーター“-pix_fmt”に相当します。例: “yuv420”、“yuv444” + FFmpegピクセルフォーマット。パラメーター“-pix_fmt”に相当します。例: “yuv420p”、“yuv444p” ビットレート FFmpegビットレート。パラメーター“-b:v”に相当します。例: “6K”、“2M” フィルター diff --git a/SpineViewer/Resources/Strings/zh.xaml b/SpineViewer/Resources/Strings/zh.xaml index 53c90de..b6f1399 100644 --- a/SpineViewer/Resources/Strings/zh.xaml +++ b/SpineViewer/Resources/Strings/zh.xaml @@ -184,7 +184,7 @@ 编码器 FFmpeg 编码器,等价于参数 “-c:v”,例如 “libx264”、“libx265” 像素格式 - FFmpeg 像素格式,等价于参数 “-pix_fmt”,例如 “yuv420”、“yuv444” + FFmpeg 像素格式,等价于参数 “-pix_fmt”,例如 “yuv420p”、“yuv444p” 比特率 FFmpeg 比特率,等价于参数 “-b:v”,例如 “6K”、“2M” 滤镜 From 311b09cc6363d28efff98c69e1bb686f1cfb32d6 Mon Sep 17 00:00:00 2001 From: ww-rm Date: Thu, 24 Jul 2025 17:58:55 +0800 Subject: [PATCH 2/8] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E5=AF=BC=E5=87=BA=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Spine/Exporters/CustomFFmpegExporter.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Spine/Exporters/CustomFFmpegExporter.cs b/Spine/Exporters/CustomFFmpegExporter.cs index 174a70b..b1c5507 100644 --- a/Spine/Exporters/CustomFFmpegExporter.cs +++ b/Spine/Exporters/CustomFFmpegExporter.cs @@ -1,5 +1,6 @@ using FFMpegCore; using FFMpegCore.Pipes; +using SFML.Graphics; using SFML.System; using System; using System.Collections.Generic; @@ -63,6 +64,22 @@ namespace Spine.Exporters else options.WithCustomArgument("-vf unpremultiply=inplace=1"); } + /// + /// 获取的一帧, 结果是预乘的 + /// + protected override SFMLImageVideoFrame GetFrame(SpineObject[] spines) + { + // BUG: 也许和 SFML 多线程或者 FFmpeg 调用有关, 当渲染线程也在运行的时候此处并行渲染会导致和 SFML 有关的内容都卡死 + // 不知道为什么用 FFmpeg 必须临时创建 RenderTexture, 否则无法正常渲染, 会导致画面帧丢失 + using var tex = new RenderTexture(_renderTexture.Size.X, _renderTexture.Size.Y); + using var view = _renderTexture.GetView(); + tex.SetView(view); + tex.Clear(_backgroundColorPma); + foreach (var sp in spines.Reverse()) tex.Draw(sp); + tex.Display(); + return new(tex.Texture.CopyToImage()); + } + public override void Export(string output, CancellationToken ct, params SpineObject[] spines) { var videoFramesSource = new RawVideoPipeSource(GetFrames(spines, output, ct)) { FrameRate = _fps }; From 1f56e2f03c405ba8e3ab0c5d902f6be46722b899 Mon Sep 17 00:00:00 2001 From: ww-rm Date: Thu, 24 Jul 2025 18:18:11 +0800 Subject: [PATCH 3/8] =?UTF-8?q?=E4=BF=AE=E6=94=B9mp4=E5=AF=BC=E5=87=BA?= =?UTF-8?q?=E5=83=8F=E7=B4=A0=E6=A0=BC=E5=BC=8F=E9=81=BF=E5=85=8D=E5=85=BC?= =?UTF-8?q?=E5=AE=B9=E6=80=A7=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Spine/Exporters/FFmpegVideoExporter.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Spine/Exporters/FFmpegVideoExporter.cs b/Spine/Exporters/FFmpegVideoExporter.cs index cce74c7..adf183b 100644 --- a/Spine/Exporters/FFmpegVideoExporter.cs +++ b/Spine/Exporters/FFmpegVideoExporter.cs @@ -126,8 +126,10 @@ namespace Spine.Exporters private void SetMp4Options(FFMpegArgumentOptions options) { + // XXX: windows 默认播放器在播放 MP4 格式时对于 libx264 编码器只支持 yuv420p 的像素格式 + // 但是如果是 libx265 则没有该限制 var customArgs = "-vf unpremultiply=inplace=1"; - options.ForceFormat("mp4").WithVideoCodec("libx264").ForcePixelFormat("yuv444p") + options.ForceFormat("mp4").WithVideoCodec("libx264").ForcePixelFormat("yuv420p") .WithFastStart() .WithConstantRateFactor(_crf) .WithCustomArgument(customArgs); From 4b64ec74c271c0bc1e52895f01ea852358f8314a Mon Sep 17 00:00:00 2001 From: ww-rm Date: Thu, 24 Jul 2025 20:38:55 +0800 Subject: [PATCH 4/8] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=A2=84=E8=A7=88?= =?UTF-8?q?=E7=94=BB=E9=9D=A2=E6=92=AD=E6=94=BE=E9=80=9F=E5=BA=A6=E5=8F=82?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SpineViewer/Models/WorkspaceModel.cs | 2 + SpineViewer/Resources/Strings/en.xaml | 1 + SpineViewer/Resources/Strings/ja.xaml | 1 + SpineViewer/Resources/Strings/zh.xaml | 1 + .../MainWindow/SFMLRendererViewModel.cs | 45 +++++++++++-------- SpineViewer/Views/MainWindow.xaml | 13 ++++-- 6 files changed, 41 insertions(+), 22 deletions(-) diff --git a/SpineViewer/Models/WorkspaceModel.cs b/SpineViewer/Models/WorkspaceModel.cs index 534d13e..88d963e 100644 --- a/SpineViewer/Models/WorkspaceModel.cs +++ b/SpineViewer/Models/WorkspaceModel.cs @@ -37,6 +37,8 @@ namespace SpineViewer.Models public uint MaxFps { get; set; } = 30; + public float Speed { get; set; } = 1f; + public bool ShowAxis { get; set; } = true; public Color BackgroundColor { get; set; } diff --git a/SpineViewer/Resources/Strings/en.xaml b/SpineViewer/Resources/Strings/en.xaml index 6d869ae..c9cc4d0 100644 --- a/SpineViewer/Resources/Strings/en.xaml +++ b/SpineViewer/Resources/Strings/en.xaml @@ -104,6 +104,7 @@ Zoom Rotation (Degrees) Max FPS + Playback Speed Render Selected Only Show Axis Background Color diff --git a/SpineViewer/Resources/Strings/ja.xaml b/SpineViewer/Resources/Strings/ja.xaml index e41ab1a..cce1f50 100644 --- a/SpineViewer/Resources/Strings/ja.xaml +++ b/SpineViewer/Resources/Strings/ja.xaml @@ -104,6 +104,7 @@ ズーム 回転(度) 最大FPS + 再生速度 選択のみレンダリング 座標軸を表示 背景色 diff --git a/SpineViewer/Resources/Strings/zh.xaml b/SpineViewer/Resources/Strings/zh.xaml index b6f1399..af3f2c2 100644 --- a/SpineViewer/Resources/Strings/zh.xaml +++ b/SpineViewer/Resources/Strings/zh.xaml @@ -104,6 +104,7 @@ 缩放 旋转(角度) 最大帧率 + 播放速度 仅渲染选中 显示坐标轴 背景颜色 diff --git a/SpineViewer/ViewModels/MainWindow/SFMLRendererViewModel.cs b/SpineViewer/ViewModels/MainWindow/SFMLRendererViewModel.cs index 54a7d60..1b2d58a 100644 --- a/SpineViewer/ViewModels/MainWindow/SFMLRendererViewModel.cs +++ b/SpineViewer/ViewModels/MainWindow/SFMLRendererViewModel.cs @@ -140,6 +140,13 @@ namespace SpineViewer.ViewModels.MainWindow set => SetProperty(_renderer.MaxFps, value, v => _renderer.MaxFps = value); } + public float Speed + { + get => _speed; + set => SetProperty(ref _speed, Math.Clamp(value, 0.01f, 100f)); + } + private float _speed = 1f; + public bool ShowAxis { get => _showAxis; @@ -193,15 +200,15 @@ namespace SpineViewer.ViewModels.MainWindow }); private RelayCommand? _cmd_Restart; - public RelayCommand Cmd_ForwardStep => _cmd_ForwardStep ??= new(() => - { - lock (_forwardDeltaLock) _forwardDelta += _renderer.MaxFps > 0 ? 1f / _renderer.MaxFps : 0.001f; + public RelayCommand Cmd_ForwardStep => _cmd_ForwardStep ??= new(() => + { + lock (_forwardDeltaLock) _forwardDelta += _renderer.MaxFps > 0 ? 1f / _renderer.MaxFps : 0.001f; }); private RelayCommand? _cmd_ForwardStep; public RelayCommand Cmd_ForwardFast => _cmd_ForwardFast ??= new(() => - { - lock (_forwardDeltaLock) _forwardDelta += _renderer.MaxFps > 0 ? 10f / _renderer.MaxFps : 0.01f; + { + lock (_forwardDeltaLock) _forwardDelta += _renderer.MaxFps > 0 ? 10f / _renderer.MaxFps : 0.01f; }); private RelayCommand? _cmd_ForwardFast; @@ -390,7 +397,7 @@ namespace SpineViewer.ViewModels.MainWindow if (_cancelToken?.IsCancellationRequested ?? true) break; // 提前中止 sp.Update(0); // 避免物理效果出现问题 - sp.Update(delta); + sp.Update(delta * _speed); // 为选中对象绘制一个半透明背景 if (sp.IsSelected) @@ -426,7 +433,7 @@ namespace SpineViewer.ViewModels.MainWindow } public RendererWorkspaceConfigModel WorkspaceConfig - { + { // TODO: 背景图片 get { @@ -441,23 +448,25 @@ namespace SpineViewer.ViewModels.MainWindow FlipX = FlipX, FlipY = FlipY, MaxFps = MaxFps, + Speed = Speed, ShowAxis = ShowAxis, BackgroundColor = BackgroundColor, }; } set { - ResolutionX = value.ResolutionX; - ResolutionY = value.ResolutionY; - CenterX = value.CenterX; - CenterY = value.CenterY; - Zoom = value.Zoom; - Rotation = value.Rotation; - FlipX = value.FlipX; - FlipY = value.FlipY; - MaxFps = value.MaxFps; - ShowAxis = value.ShowAxis; - BackgroundColor = value.BackgroundColor; + ResolutionX = value.ResolutionX; + ResolutionY = value.ResolutionY; + CenterX = value.CenterX; + CenterY = value.CenterY; + Zoom = value.Zoom; + Rotation = value.Rotation; + FlipX = value.FlipX; + FlipY = value.FlipY; + MaxFps = value.MaxFps; + Speed = value.Speed; + ShowAxis = value.ShowAxis; + BackgroundColor = value.BackgroundColor; } } } diff --git a/SpineViewer/Views/MainWindow.xaml b/SpineViewer/Views/MainWindow.xaml index b9db125..90625c3 100644 --- a/SpineViewer/Views/MainWindow.xaml +++ b/SpineViewer/Views/MainWindow.xaml @@ -693,6 +693,7 @@ + @@ -731,13 +732,17 @@