Merge pull request #68 from ww-rm/dev/wpf

This commit is contained in:
ww-rm
2025-07-24 21:36:56 +08:00
committed by GitHub
20 changed files with 159 additions and 62 deletions

View File

@@ -1,5 +1,13 @@
# CHANGELOG # CHANGELOG
## v0.15.5
- 修复自定义导出时的画面错误
- 设置 mp4 像素格式为 yuv420p 避免 windows 默认播放器无法打开
- 增加预览画面和导出时的速度参数设置
- 修复一些提示文本错误
- 导出时自动将分辨率向下调整为 2 的倍数, 避免 yuv420p 格式出错
## v0.15.4 ## v0.15.4
- 修复导出时可能的卡死问题 - 修复导出时可能的卡死问题

View File

@@ -31,6 +31,9 @@ namespace Spine.Exporters
/// <param name="height">画布高像素值</param> /// <param name="height">画布高像素值</param>
public BaseExporter(uint width , uint height) public BaseExporter(uint width , uint height)
{ {
// XXX: 强制变成 2 的倍数, 防止像是 yuv420p 这种像素格式报错
width = width >> 1 << 1;
height = height >> 1 << 1;
if (width <= 0 || height <= 0) if (width <= 0 || height <= 0)
throw new ArgumentException($"Invalid resolution: {width}, {height}"); throw new ArgumentException($"Invalid resolution: {width}, {height}");
_renderTexture = new(width, height); _renderTexture = new(width, height);
@@ -42,6 +45,9 @@ namespace Spine.Exporters
/// </summary> /// </summary>
public BaseExporter(Vector2u resolution) public BaseExporter(Vector2u resolution)
{ {
// XXX: 强制变成 2 的倍数, 防止像是 yuv420p 这种像素格式报错
resolution.X = resolution.X >> 1 << 1;
resolution.Y = resolution.Y >> 1 << 1;
if (resolution.X <= 0 || resolution.Y <= 0) if (resolution.X <= 0 || resolution.Y <= 0)
throw new ArgumentException($"Invalid resolution: {resolution}"); throw new ArgumentException($"Invalid resolution: {resolution}");
_renderTexture = new(resolution.X, resolution.Y); _renderTexture = new(resolution.X, resolution.Y);
@@ -92,6 +98,9 @@ namespace Spine.Exporters
get => _renderTexture.Size; get => _renderTexture.Size;
set set
{ {
// XXX: 强制变成 2 的倍数, 防止像是 yuv420p 这种像素格式报错
value.X = value.X >> 1 << 1;
value.Y = value.Y >> 1 << 1;
if (value.X <= 0 || value.Y <= 0) if (value.X <= 0 || value.Y <= 0)
{ {
_logger.Warn("Omit invalid exporter resolution: {0}", value); _logger.Warn("Omit invalid exporter resolution: {0}", value);

View File

@@ -1,5 +1,6 @@
using FFMpegCore; using FFMpegCore;
using FFMpegCore.Pipes; using FFMpegCore.Pipes;
using SFML.Graphics;
using SFML.System; using SFML.System;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -63,6 +64,22 @@ namespace Spine.Exporters
else options.WithCustomArgument("-vf unpremultiply=inplace=1"); else options.WithCustomArgument("-vf unpremultiply=inplace=1");
} }
/// <summary>
/// 获取的一帧, 结果是预乘的
/// </summary>
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) public override void Export(string output, CancellationToken ct, params SpineObject[] spines)
{ {
var videoFramesSource = new RawVideoPipeSource(GetFrames(spines, output, ct)) { FrameRate = _fps }; var videoFramesSource = new RawVideoPipeSource(GetFrames(spines, output, ct)) { FrameRate = _fps };

View File

@@ -126,8 +126,10 @@ namespace Spine.Exporters
private void SetMp4Options(FFMpegArgumentOptions options) private void SetMp4Options(FFMpegArgumentOptions options)
{ {
// XXX: windows 默认播放器在播放 MP4 格式时对于 libx264 编码器只支持 yuv420p 的像素格式
// 但是如果是 libx265 则没有该限制
var customArgs = "-vf unpremultiply=inplace=1"; var customArgs = "-vf unpremultiply=inplace=1";
options.ForceFormat("mp4").WithVideoCodec("libx264").ForcePixelFormat("yuv444p") options.ForceFormat("mp4").WithVideoCodec("libx264").ForcePixelFormat("yuv420p")
.WithFastStart() .WithFastStart()
.WithConstantRateFactor(_crf) .WithConstantRateFactor(_crf)
.WithCustomArgument(customArgs); .WithCustomArgument(customArgs);

View File

@@ -55,6 +55,21 @@ namespace Spine.Exporters
} }
protected float _fps = 24; protected float _fps = 24;
public float Speed
{
get => _speed;
set
{
if (_speed <= 0)
{
_logger.Warn("Omit invalid speed: {0}", value);
return;
}
_speed = value;
}
}
protected float _speed = 1f;
/// <summary> /// <summary>
/// 是否保留最后一帧 /// 是否保留最后一帧
/// </summary> /// </summary>
@@ -92,7 +107,7 @@ namespace Spine.Exporters
// 导出完整帧 // 导出完整帧
for (int i = 0; i < total; i++) for (int i = 0; i < total; i++)
{ {
foreach (var spine in spines) spine.Update(delta); foreach (var spine in spines) spine.Update(delta * _speed);
yield return GetFrame(spines); yield return GetFrame(spines);
} }
@@ -100,7 +115,7 @@ namespace Spine.Exporters
if (hasFinal) if (hasFinal)
{ {
// XXX: 此处还是按照完整的一帧时长进行更新, 也许可以只更新准确的最后一帧时长 // XXX: 此处还是按照完整的一帧时长进行更新, 也许可以只更新准确的最后一帧时长
foreach (var spine in spines) spine.Update(delta); foreach (var spine in spines) spine.Update(delta * _speed);
yield return GetFrame(spines); yield return GetFrame(spines);
} }
} }

View File

@@ -7,7 +7,7 @@
<TargetFramework>net8.0-windows</TargetFramework> <TargetFramework>net8.0-windows</TargetFramework>
<BaseOutputPath>$(SolutionDir)out</BaseOutputPath> <BaseOutputPath>$(SolutionDir)out</BaseOutputPath>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion> <IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<Version>0.15.4</Version> <Version>0.15.5</Version>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>

View File

@@ -37,6 +37,8 @@ namespace SpineViewer.Models
public uint MaxFps { get; set; } = 30; public uint MaxFps { get; set; } = 30;
public float Speed { get; set; } = 1f;
public bool ShowAxis { get; set; } = true; public bool ShowAxis { get; set; } = true;
public Color BackgroundColor { get; set; } public Color BackgroundColor { get; set; }

View File

@@ -104,6 +104,7 @@
<s:String x:Key="Str_Zoom">Zoom</s:String> <s:String x:Key="Str_Zoom">Zoom</s:String>
<s:String x:Key="Str_Rotation">Rotation (Degrees)</s:String> <s:String x:Key="Str_Rotation">Rotation (Degrees)</s:String>
<s:String x:Key="Str_MaxFps">Max FPS</s:String> <s:String x:Key="Str_MaxFps">Max FPS</s:String>
<s:String x:Key="Str_PlaySpeed">Playback Speed</s:String>
<s:String x:Key="Str_RenderSelectedOnly">Render Selected Only</s:String> <s:String x:Key="Str_RenderSelectedOnly">Render Selected Only</s:String>
<s:String x:Key="Str_ShowAxis">Show Axis</s:String> <s:String x:Key="Str_ShowAxis">Show Axis</s:String>
<s:String x:Key="Str_BackgroundColor">Background Color</s:String> <s:String x:Key="Str_BackgroundColor">Background Color</s:String>
@@ -166,6 +167,8 @@
<s:String x:Key="Str_ExportDurationTooltip">Export duration; if less than 0, the maximum duration of all animations in all models will be used during export.</s:String> <s:String x:Key="Str_ExportDurationTooltip">Export duration; if less than 0, the maximum duration of all animations in all models will be used during export.</s:String>
<s:String x:Key="Str_Fps">FPS</s:String> <s:String x:Key="Str_Fps">FPS</s:String>
<s:String x:Key="Str_ExportSpeed">Export Speed</s:String>
<s:String x:Key="Str_ExportSpeedTooltip">Export speed factor; only affects the animation speed of the model, not the export duration or frame rate.</s:String>
<s:String x:Key="Str_KeepLastFrame">Keep Last Frame</s:String> <s:String x:Key="Str_KeepLastFrame">Keep Last Frame</s:String>
<s:String x:Key="Str_KeepLastFrameTooltip">When keeping the last frame, animation is smoother but frame count may be one higher</s:String> <s:String x:Key="Str_KeepLastFrameTooltip">When keeping the last frame, animation is smoother but frame count may be one higher</s:String>
@@ -184,7 +187,7 @@
<s:String x:Key="Str_FFmpegCodec">Codec</s:String> <s:String x:Key="Str_FFmpegCodec">Codec</s:String>
<s:String x:Key="Str_FFmpegCodecTooltip">FFmpeg codec (equivalent to "-c:v"), e.g. "libx264", "libx265"</s:String> <s:String x:Key="Str_FFmpegCodecTooltip">FFmpeg codec (equivalent to "-c:v"), e.g. "libx264", "libx265"</s:String>
<s:String x:Key="Str_FFmpegPixelFormat">Pixel Format</s:String> <s:String x:Key="Str_FFmpegPixelFormat">Pixel Format</s:String>
<s:String x:Key="Str_FFmpegPixelFormatTooltip">FFmpeg pixel format (equivalent to "-pix_fmt"), e.g. "yuv420", "yuv444"</s:String> <s:String x:Key="Str_FFmpegPixelFormatTooltip">FFmpeg pixel format (equivalent to "-pix_fmt"), e.g. "yuv420p", "yuv444p"</s:String>
<s:String x:Key="Str_FFmpegBitrate">Bitrate</s:String> <s:String x:Key="Str_FFmpegBitrate">Bitrate</s:String>
<s:String x:Key="Str_FFmpegBitrateTooltip">FFmpeg bitrate (equivalent to "-b:v"), e.g. "6K", "2M"</s:String> <s:String x:Key="Str_FFmpegBitrateTooltip">FFmpeg bitrate (equivalent to "-b:v"), e.g. "6K", "2M"</s:String>
<s:String x:Key="Str_FFmpegFilter">Filter</s:String> <s:String x:Key="Str_FFmpegFilter">Filter</s:String>

View File

@@ -104,6 +104,7 @@
<s:String x:Key="Str_Zoom">ズーム</s:String> <s:String x:Key="Str_Zoom">ズーム</s:String>
<s:String x:Key="Str_Rotation">回転(度)</s:String> <s:String x:Key="Str_Rotation">回転(度)</s:String>
<s:String x:Key="Str_MaxFps">最大FPS</s:String> <s:String x:Key="Str_MaxFps">最大FPS</s:String>
<s:String x:Key="Str_PlaySpeed">再生速度</s:String>
<s:String x:Key="Str_RenderSelectedOnly">選択のみレンダリング</s:String> <s:String x:Key="Str_RenderSelectedOnly">選択のみレンダリング</s:String>
<s:String x:Key="Str_ShowAxis">座標軸を表示</s:String> <s:String x:Key="Str_ShowAxis">座標軸を表示</s:String>
<s:String x:Key="Str_BackgroundColor">背景色</s:String> <s:String x:Key="Str_BackgroundColor">背景色</s:String>
@@ -166,6 +167,8 @@
<s:String x:Key="Str_ExportDurationTooltip">エクスポート時間。0 未満の場合、エクスポート時にすべてのモデルのすべてのアニメーションの最大時間が使用されます。</s:String> <s:String x:Key="Str_ExportDurationTooltip">エクスポート時間。0 未満の場合、エクスポート時にすべてのモデルのすべてのアニメーションの最大時間が使用されます。</s:String>
<s:String x:Key="Str_Fps">FPS</s:String> <s:String x:Key="Str_Fps">FPS</s:String>
<s:String x:Key="Str_ExportSpeed">エクスポート速度</s:String>
<s:String x:Key="Str_ExportSpeedTooltip">エクスポート速度係数。モデルの動作速度のみに影響し、エクスポート時間やフレームレートなどには影響しません。</s:String>
<s:String x:Key="Str_KeepLastFrame">最後のフレームを保持</s:String> <s:String x:Key="Str_KeepLastFrame">最後のフレームを保持</s:String>
<s:String x:Key="Str_KeepLastFrameTooltip">最後のフレームを保持すると、アニメーションはより連続して見えますが、フレーム数が予想より1フレーム多くなる可能性があります</s:String> <s:String x:Key="Str_KeepLastFrameTooltip">最後のフレームを保持すると、アニメーションはより連続して見えますが、フレーム数が予想より1フレーム多くなる可能性があります</s:String>
@@ -184,7 +187,7 @@
<s:String x:Key="Str_FFmpegCodec">コーデック</s:String> <s:String x:Key="Str_FFmpegCodec">コーデック</s:String>
<s:String x:Key="Str_FFmpegCodecTooltip">FFmpegコーデック。パラメーター“-c:v”に相当します。例: “libx264”、“libx265”</s:String> <s:String x:Key="Str_FFmpegCodecTooltip">FFmpegコーデック。パラメーター“-c:v”に相当します。例: “libx264”、“libx265”</s:String>
<s:String x:Key="Str_FFmpegPixelFormat">ピクセルフォーマット</s:String> <s:String x:Key="Str_FFmpegPixelFormat">ピクセルフォーマット</s:String>
<s:String x:Key="Str_FFmpegPixelFormatTooltip">FFmpegピクセルフォーマット。パラメーター“-pix_fmt”に相当します。例: “yuv420”、“yuv444”</s:String> <s:String x:Key="Str_FFmpegPixelFormatTooltip">FFmpegピクセルフォーマット。パラメーター“-pix_fmt”に相当します。例: “yuv420p”、“yuv444p”</s:String>
<s:String x:Key="Str_FFmpegBitrate">ビットレート</s:String> <s:String x:Key="Str_FFmpegBitrate">ビットレート</s:String>
<s:String x:Key="Str_FFmpegBitrateTooltip">FFmpegビットレート。パラメーター“-b:v”に相当します。例: “6K”、“2M”</s:String> <s:String x:Key="Str_FFmpegBitrateTooltip">FFmpegビットレート。パラメーター“-b:v”に相当します。例: “6K”、“2M”</s:String>
<s:String x:Key="Str_FFmpegFilter">フィルター</s:String> <s:String x:Key="Str_FFmpegFilter">フィルター</s:String>

View File

@@ -104,6 +104,7 @@
<s:String x:Key="Str_Zoom">缩放</s:String> <s:String x:Key="Str_Zoom">缩放</s:String>
<s:String x:Key="Str_Rotation">旋转(角度)</s:String> <s:String x:Key="Str_Rotation">旋转(角度)</s:String>
<s:String x:Key="Str_MaxFps">最大帧率</s:String> <s:String x:Key="Str_MaxFps">最大帧率</s:String>
<s:String x:Key="Str_PlaySpeed">播放速度</s:String>
<s:String x:Key="Str_RenderSelectedOnly">仅渲染选中</s:String> <s:String x:Key="Str_RenderSelectedOnly">仅渲染选中</s:String>
<s:String x:Key="Str_ShowAxis">显示坐标轴</s:String> <s:String x:Key="Str_ShowAxis">显示坐标轴</s:String>
<s:String x:Key="Str_BackgroundColor">背景颜色</s:String> <s:String x:Key="Str_BackgroundColor">背景颜色</s:String>
@@ -166,6 +167,8 @@
<s:String x:Key="Str_ExportDurationTooltip">导出时长,如果小于 0则在导出时使用所有模型所有动画的最大时长</s:String> <s:String x:Key="Str_ExportDurationTooltip">导出时长,如果小于 0则在导出时使用所有模型所有动画的最大时长</s:String>
<s:String x:Key="Str_Fps">帧率</s:String> <s:String x:Key="Str_Fps">帧率</s:String>
<s:String x:Key="Str_ExportSpeed">导出速度</s:String>
<s:String x:Key="Str_ExportSpeedTooltip">导出速度因子, 仅影响模型的动作速度, 不影响导出时长和帧率等参数</s:String>
<s:String x:Key="Str_KeepLastFrame">保留最后一帧</s:String> <s:String x:Key="Str_KeepLastFrame">保留最后一帧</s:String>
<s:String x:Key="Str_KeepLastFrameTooltip">当设置保留最后一帧时,动图会更为连贯,但是帧数可能比预期帧数多 1</s:String> <s:String x:Key="Str_KeepLastFrameTooltip">当设置保留最后一帧时,动图会更为连贯,但是帧数可能比预期帧数多 1</s:String>
@@ -184,7 +187,7 @@
<s:String x:Key="Str_FFmpegCodec">编码器</s:String> <s:String x:Key="Str_FFmpegCodec">编码器</s:String>
<s:String x:Key="Str_FFmpegCodecTooltip">FFmpeg 编码器,等价于参数 “-c:v”例如 “libx264”、“libx265”</s:String> <s:String x:Key="Str_FFmpegCodecTooltip">FFmpeg 编码器,等价于参数 “-c:v”例如 “libx264”、“libx265”</s:String>
<s:String x:Key="Str_FFmpegPixelFormat">像素格式</s:String> <s:String x:Key="Str_FFmpegPixelFormat">像素格式</s:String>
<s:String x:Key="Str_FFmpegPixelFormatTooltip">FFmpeg 像素格式,等价于参数 “-pix_fmt”例如 “yuv420”、“yuv444”</s:String> <s:String x:Key="Str_FFmpegPixelFormatTooltip">FFmpeg 像素格式,等价于参数 “-pix_fmt”例如 “yuv420p”、“yuv444p”</s:String>
<s:String x:Key="Str_FFmpegBitrate">比特率</s:String> <s:String x:Key="Str_FFmpegBitrate">比特率</s:String>
<s:String x:Key="Str_FFmpegBitrateTooltip">FFmpeg 比特率,等价于参数 “-b:v”例如 “6K”、“2M”</s:String> <s:String x:Key="Str_FFmpegBitrateTooltip">FFmpeg 比特率,等价于参数 “-b:v”例如 “6K”、“2M”</s:String>
<s:String x:Key="Str_FFmpegFilter">滤镜</s:String> <s:String x:Key="Str_FFmpegFilter">滤镜</s:String>

View File

@@ -7,7 +7,7 @@
<TargetFramework>net8.0-windows</TargetFramework> <TargetFramework>net8.0-windows</TargetFramework>
<BaseOutputPath>$(SolutionDir)out</BaseOutputPath> <BaseOutputPath>$(SolutionDir)out</BaseOutputPath>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion> <IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<Version>0.15.4</Version> <Version>0.15.5</Version>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
</PropertyGroup> </PropertyGroup>

View File

@@ -64,6 +64,7 @@ namespace SpineViewer.ViewModels.Exporters
{ {
BackgroundColor = new(_backgroundColor.R, _backgroundColor.G, _backgroundColor.B, _backgroundColor.A), BackgroundColor = new(_backgroundColor.R, _backgroundColor.G, _backgroundColor.B, _backgroundColor.A),
Fps = _fps, Fps = _fps,
Speed = _speed,
KeepLast = _keepLast, KeepLast = _keepLast,
Format = _format, Format = _format,
Codec = _codec, Codec = _codec,

View File

@@ -54,6 +54,7 @@ namespace SpineViewer.ViewModels.Exporters
{ {
BackgroundColor = new(_backgroundColor.R, _backgroundColor.G, _backgroundColor.B, _backgroundColor.A), BackgroundColor = new(_backgroundColor.R, _backgroundColor.G, _backgroundColor.B, _backgroundColor.A),
Fps = _fps, Fps = _fps,
Speed = _speed,
KeepLast = _keepLast, KeepLast = _keepLast,
Format = _format, Format = _format,
Loop = _loop, Loop = _loop,

View File

@@ -34,6 +34,7 @@ namespace SpineViewer.ViewModels.Exporters
{ {
BackgroundColor = new(_backgroundColor.R, _backgroundColor.G, _backgroundColor.B, _backgroundColor.A), BackgroundColor = new(_backgroundColor.R, _backgroundColor.G, _backgroundColor.B, _backgroundColor.A),
Fps = _fps, Fps = _fps,
Speed = _speed,
KeepLast = _keepLast KeepLast = _keepLast
}; };

View File

@@ -16,6 +16,9 @@ namespace SpineViewer.ViewModels.Exporters
public uint Fps { get => _fps; set => SetProperty(ref _fps, Math.Max(1, value)); } public uint Fps { get => _fps; set => SetProperty(ref _fps, Math.Max(1, value)); }
protected uint _fps = 30; protected uint _fps = 30;
public float Speed { get => _speed; set => SetProperty(ref _speed, Math.Clamp(value, 0.001f, 1000f)); }
protected float _speed = 1f;
public bool KeepLast { get => _keepLast; set => SetProperty(ref _keepLast, value); } public bool KeepLast { get => _keepLast; set => SetProperty(ref _keepLast, value); }
protected bool _keepLast = true; protected bool _keepLast = true;
} }

View File

@@ -140,6 +140,13 @@ namespace SpineViewer.ViewModels.MainWindow
set => SetProperty(_renderer.MaxFps, value, v => _renderer.MaxFps = value); 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 public bool ShowAxis
{ {
get => _showAxis; get => _showAxis;
@@ -193,15 +200,15 @@ namespace SpineViewer.ViewModels.MainWindow
}); });
private RelayCommand? _cmd_Restart; private RelayCommand? _cmd_Restart;
public RelayCommand Cmd_ForwardStep => _cmd_ForwardStep ??= new(() => public RelayCommand Cmd_ForwardStep => _cmd_ForwardStep ??= new(() =>
{ {
lock (_forwardDeltaLock) _forwardDelta += _renderer.MaxFps > 0 ? 1f / _renderer.MaxFps : 0.001f; lock (_forwardDeltaLock) _forwardDelta += _renderer.MaxFps > 0 ? 1f / _renderer.MaxFps : 0.001f;
}); });
private RelayCommand? _cmd_ForwardStep; private RelayCommand? _cmd_ForwardStep;
public RelayCommand Cmd_ForwardFast => _cmd_ForwardFast ??= new(() => 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; private RelayCommand? _cmd_ForwardFast;
@@ -390,7 +397,7 @@ namespace SpineViewer.ViewModels.MainWindow
if (_cancelToken?.IsCancellationRequested ?? true) break; // 提前中止 if (_cancelToken?.IsCancellationRequested ?? true) break; // 提前中止
sp.Update(0); // 避免物理效果出现问题 sp.Update(0); // 避免物理效果出现问题
sp.Update(delta); sp.Update(delta * _speed);
// 为选中对象绘制一个半透明背景 // 为选中对象绘制一个半透明背景
if (sp.IsSelected) if (sp.IsSelected)
@@ -426,7 +433,7 @@ namespace SpineViewer.ViewModels.MainWindow
} }
public RendererWorkspaceConfigModel WorkspaceConfig public RendererWorkspaceConfigModel WorkspaceConfig
{ {
// TODO: 背景图片 // TODO: 背景图片
get get
{ {
@@ -441,23 +448,25 @@ namespace SpineViewer.ViewModels.MainWindow
FlipX = FlipX, FlipX = FlipX,
FlipY = FlipY, FlipY = FlipY,
MaxFps = MaxFps, MaxFps = MaxFps,
Speed = Speed,
ShowAxis = ShowAxis, ShowAxis = ShowAxis,
BackgroundColor = BackgroundColor, BackgroundColor = BackgroundColor,
}; };
} }
set set
{ {
ResolutionX = value.ResolutionX; ResolutionX = value.ResolutionX;
ResolutionY = value.ResolutionY; ResolutionY = value.ResolutionY;
CenterX = value.CenterX; CenterX = value.CenterX;
CenterY = value.CenterY; CenterY = value.CenterY;
Zoom = value.Zoom; Zoom = value.Zoom;
Rotation = value.Rotation; Rotation = value.Rotation;
FlipX = value.FlipX; FlipX = value.FlipX;
FlipY = value.FlipY; FlipY = value.FlipY;
MaxFps = value.MaxFps; MaxFps = value.MaxFps;
ShowAxis = value.ShowAxis; Speed = value.Speed;
BackgroundColor = value.BackgroundColor; ShowAxis = value.ShowAxis;
BackgroundColor = value.BackgroundColor;
} }
} }
} }

View File

@@ -67,6 +67,7 @@
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="60"/> <RowDefinition Height="60"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
@@ -122,38 +123,42 @@
<Label Grid.Row="10" Grid.Column="0" Content="{DynamicResource Str_Fps}"/> <Label Grid.Row="10" Grid.Column="0" Content="{DynamicResource Str_Fps}"/>
<TextBox Grid.Row="10" Grid.Column="1" Text="{Binding Fps}"/> <TextBox Grid.Row="10" Grid.Column="1" Text="{Binding Fps}"/>
<!-- 是否保留最后一帧 --> <!-- 导出速度 -->
<Label Grid.Row="11" Grid.Column="0" Content="{DynamicResource Str_KeepLastFrame}" ToolTip="{DynamicResource Str_KeepLastFrameTooltip}"/> <Label Grid.Row="11" Grid.Column="0" Content="{DynamicResource Str_ExportSpeed}" ToolTip="{DynamicResource Str_ExportSpeedTooltip}"/>
<ToggleButton Grid.Row="11" Grid.Column="1" IsChecked="{Binding KeepLast}" ToolTip="{DynamicResource Str_KeelLastFrameTooltip}"/> <TextBox Grid.Row="11" Grid.Column="1" Text="{Binding Speed}" ToolTip="{DynamicResource Str_ExportSpeedTooltip}"/>
<Separator Grid.Row="12" Grid.ColumnSpan="2" Height="10"/> <!-- 是否保留最后一帧 -->
<Label Grid.Row="12" Grid.Column="0" Content="{DynamicResource Str_KeepLastFrame}" ToolTip="{DynamicResource Str_KeepLastFrameTooltip}"/>
<ToggleButton Grid.Row="12" Grid.Column="1" IsChecked="{Binding KeepLast}" ToolTip="{DynamicResource Str_KeelLastFrameTooltip}"/>
<Separator Grid.Row="13" Grid.ColumnSpan="2" Height="10"/>
<!-- 导出格式 --> <!-- 导出格式 -->
<Label Grid.Row="13" Grid.Column="0" Content="{DynamicResource Str_FFmpegFormat}" ToolTip="{DynamicResource Str_FFmpegFormatTooltip}"/> <Label Grid.Row="14" Grid.Column="0" Content="{DynamicResource Str_FFmpegFormat}" ToolTip="{DynamicResource Str_FFmpegFormatTooltip}"/>
<TextBox Grid.Row="13" Grid.Column="1" Text="{Binding Format}" ToolTip="{DynamicResource Str_FFmpegFormatTooltip}"/> <TextBox Grid.Row="14" Grid.Column="1" Text="{Binding Format}" ToolTip="{DynamicResource Str_FFmpegFormatTooltip}"/>
<!-- 编码器 --> <!-- 编码器 -->
<Label Grid.Row="14" Grid.Column="0" Content="{DynamicResource Str_FFmpegCodec}" ToolTip="{DynamicResource Str_FFmpegCodecTooltip}"/> <Label Grid.Row="15" Grid.Column="0" Content="{DynamicResource Str_FFmpegCodec}" ToolTip="{DynamicResource Str_FFmpegCodecTooltip}"/>
<TextBox Grid.Row="14" Grid.Column="1" Text="{Binding Codec}" ToolTip="{DynamicResource Str_FFmpegCodecTooltip}"/> <TextBox Grid.Row="15" Grid.Column="1" Text="{Binding Codec}" ToolTip="{DynamicResource Str_FFmpegCodecTooltip}"/>
<!-- 像素格式 --> <!-- 像素格式 -->
<Label Grid.Row="15" Grid.Column="0" Content="{DynamicResource Str_FFmpegPixelFormat}" ToolTip="{DynamicResource Str_FFmpegPixelFormatTooltip}"/> <Label Grid.Row="16" Grid.Column="0" Content="{DynamicResource Str_FFmpegPixelFormat}" ToolTip="{DynamicResource Str_FFmpegPixelFormatTooltip}"/>
<TextBox Grid.Row="15" Grid.Column="1" Text="{Binding PixelFormat}" ToolTip="{DynamicResource Str_FFmpegPixelFormatTooltip}"/> <TextBox Grid.Row="16" Grid.Column="1" Text="{Binding PixelFormat}" ToolTip="{DynamicResource Str_FFmpegPixelFormatTooltip}"/>
<!-- 比特率 --> <!-- 比特率 -->
<Label Grid.Row="16" Grid.Column="0" Content="{DynamicResource Str_FFmpegBitrate}" ToolTip="{DynamicResource Str_FFmpegBitrateTooltip}"/> <Label Grid.Row="17" Grid.Column="0" Content="{DynamicResource Str_FFmpegBitrate}" ToolTip="{DynamicResource Str_FFmpegBitrateTooltip}"/>
<TextBox Grid.Row="16" Grid.Column="1" Text="{Binding Bitrate}" ToolTip="{DynamicResource Str_FFmpegBitrateTooltip}"/> <TextBox Grid.Row="17" Grid.Column="1" Text="{Binding Bitrate}" ToolTip="{DynamicResource Str_FFmpegBitrateTooltip}"/>
<!-- 滤镜 --> <!-- 滤镜 -->
<Label Grid.Row="17" Grid.Column="0" Content="{DynamicResource Str_FFmpegFilter}" ToolTip="{DynamicResource Str_FFmpegFilterTooltip}"/> <Label Grid.Row="18" Grid.Column="0" Content="{DynamicResource Str_FFmpegFilter}" ToolTip="{DynamicResource Str_FFmpegFilterTooltip}"/>
<TextBox Grid.Row="17" Grid.Column="1" Text="{Binding Filter}" ToolTip="{DynamicResource Str_FFmpegFilterTooltip}"/> <TextBox Grid.Row="18" Grid.Column="1" Text="{Binding Filter}" ToolTip="{DynamicResource Str_FFmpegFilterTooltip}"/>
<!-- 自定义参数 --> <!-- 自定义参数 -->
<Label Grid.Row="18" Grid.Column="0" <Label Grid.Row="19" Grid.Column="0"
VerticalAlignment="Top" VerticalAlignment="Top"
Content="{DynamicResource Str_FFmpegCustomArgs}" Content="{DynamicResource Str_FFmpegCustomArgs}"
ToolTip="{DynamicResource Str_FFmpegCustomArgsTooltip}"/> ToolTip="{DynamicResource Str_FFmpegCustomArgsTooltip}"/>
<TextBox Grid.Row="18" Grid.Column="1" <TextBox Grid.Row="19" Grid.Column="1"
HorizontalContentAlignment="Left" HorizontalContentAlignment="Left"
VerticalContentAlignment="Top" VerticalContentAlignment="Top"
TextWrapping="Wrap" TextWrapping="Wrap"

View File

@@ -67,6 +67,7 @@
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<!-- 水平分辨率 --> <!-- 水平分辨率 -->
@@ -121,31 +122,35 @@
<Label Grid.Row="10" Grid.Column="0" Content="{DynamicResource Str_Fps}"/> <Label Grid.Row="10" Grid.Column="0" Content="{DynamicResource Str_Fps}"/>
<TextBox Grid.Row="10" Grid.Column="1" Text="{Binding Fps}"/> <TextBox Grid.Row="10" Grid.Column="1" Text="{Binding Fps}"/>
<!-- 是否保留最后一帧 --> <!-- 导出速度 -->
<Label Grid.Row="11" Grid.Column="0" Content="{DynamicResource Str_KeepLastFrame}" ToolTip="{DynamicResource Str_KeepLastFrameTooltip}"/> <Label Grid.Row="11" Grid.Column="0" Content="{DynamicResource Str_ExportSpeed}" ToolTip="{DynamicResource Str_ExportSpeedTooltip}"/>
<ToggleButton Grid.Row="11" Grid.Column="1" IsChecked="{Binding KeepLast}" ToolTip="{DynamicResource Str_KeelLastFrameTooltip}"/> <TextBox Grid.Row="11" Grid.Column="1" Text="{Binding Speed}" ToolTip="{DynamicResource Str_ExportSpeedTooltip}"/>
<Separator Grid.Row="12" Grid.ColumnSpan="2" Height="10"/> <!-- 是否保留最后一帧 -->
<Label Grid.Row="12" Grid.Column="0" Content="{DynamicResource Str_KeepLastFrame}" ToolTip="{DynamicResource Str_KeepLastFrameTooltip}"/>
<ToggleButton Grid.Row="12" Grid.Column="1" IsChecked="{Binding KeepLast}" ToolTip="{DynamicResource Str_KeelLastFrameTooltip}"/>
<Separator Grid.Row="13" Grid.ColumnSpan="2" Height="10"/>
<!-- 视频格式 --> <!-- 视频格式 -->
<Label Grid.Row="13" Grid.Column="0" Content="{DynamicResource Str_VideoFormat}"/> <Label Grid.Row="14" Grid.Column="0" Content="{DynamicResource Str_VideoFormat}"/>
<ComboBox Grid.Row="13" Grid.Column="1" SelectedItem="{Binding Format}" ItemsSource="{Binding VideoFormatOptions}"/> <ComboBox Grid.Row="14" Grid.Column="1" SelectedItem="{Binding Format}" ItemsSource="{Binding VideoFormatOptions}"/>
<!-- 动图是否循环 --> <!-- 动图是否循环 -->
<Label Grid.Row="14" Grid.Column="0" Content="{DynamicResource Str_LoopPlay}" ToolTip="{DynamicResource Str_LoopPlayTooltip}"/> <Label Grid.Row="15" Grid.Column="0" Content="{DynamicResource Str_LoopPlay}" ToolTip="{DynamicResource Str_LoopPlayTooltip}"/>
<ToggleButton Grid.Row="14" Grid.Column="1" IsChecked="{Binding Loop}" ToolTip="{DynamicResource Str_LoopPlayTooltip}"/> <ToggleButton Grid.Row="15" Grid.Column="1" IsChecked="{Binding Loop}" ToolTip="{DynamicResource Str_LoopPlayTooltip}"/>
<!-- 质量参数 --> <!-- 质量参数 -->
<Label Grid.Row="15" Grid.Column="0" Content="{DynamicResource Str_QualityParameter}" ToolTip="{DynamicResource Str_QualityParameterTooltip}"/> <Label Grid.Row="16" Grid.Column="0" Content="{DynamicResource Str_QualityParameter}" ToolTip="{DynamicResource Str_QualityParameterTooltip}"/>
<TextBox Grid.Row="15" Grid.Column="1" Text="{Binding Quality}" ToolTip="{DynamicResource Str_QualityParameterTooltip}"/> <TextBox Grid.Row="16" Grid.Column="1" Text="{Binding Quality}" ToolTip="{DynamicResource Str_QualityParameterTooltip}"/>
<!-- 无损压缩 --> <!-- 无损压缩 -->
<Label Grid.Row="16" Grid.Column="0" Content="{DynamicResource Str_LosslessParam}" ToolTip="{DynamicResource Str_LosslessParamTooltip}"/> <Label Grid.Row="17" Grid.Column="0" Content="{DynamicResource Str_LosslessParam}" ToolTip="{DynamicResource Str_LosslessParamTooltip}"/>
<ToggleButton Grid.Row="16" Grid.Column="1" IsChecked="{Binding Lossless}" ToolTip="{DynamicResource Str_LosslessParamTooltip}"/> <ToggleButton Grid.Row="17" Grid.Column="1" IsChecked="{Binding Lossless}" ToolTip="{DynamicResource Str_LosslessParamTooltip}"/>
<!-- CRF 参数 --> <!-- CRF 参数 -->
<Label Grid.Row="17" Grid.Column="0" Content="{DynamicResource Str_CrfParameter}" ToolTip="{DynamicResource Str_CrfParameterTooltip}"/> <Label Grid.Row="18" Grid.Column="0" Content="{DynamicResource Str_CrfParameter}" ToolTip="{DynamicResource Str_CrfParameterTooltip}"/>
<TextBox Grid.Row="17" Grid.Column="1" Text="{Binding Crf}" ToolTip="{DynamicResource Str_CrfParameterTooltip}"/> <TextBox Grid.Row="18" Grid.Column="1" Text="{Binding Crf}" ToolTip="{DynamicResource Str_CrfParameterTooltip}"/>
</Grid> </Grid>
</ScrollViewer> </ScrollViewer>

View File

@@ -62,6 +62,7 @@
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<!-- 水平分辨率 --> <!-- 水平分辨率 -->
@@ -116,9 +117,13 @@
<Label Grid.Row="10" Grid.Column="0" Content="{DynamicResource Str_Fps}"/> <Label Grid.Row="10" Grid.Column="0" Content="{DynamicResource Str_Fps}"/>
<TextBox Grid.Row="10" Grid.Column="1" Text="{Binding Fps}"/> <TextBox Grid.Row="10" Grid.Column="1" Text="{Binding Fps}"/>
<!-- 导出速度 -->
<Label Grid.Row="11" Grid.Column="0" Content="{DynamicResource Str_ExportSpeed}" ToolTip="{DynamicResource Str_ExportSpeedTooltip}"/>
<TextBox Grid.Row="11" Grid.Column="1" Text="{Binding Speed}" ToolTip="{DynamicResource Str_ExportSpeedTooltip}"/>
<!-- 是否保留最后一帧 --> <!-- 是否保留最后一帧 -->
<Label Grid.Row="11" Grid.Column="0" Content="{DynamicResource Str_KeepLastFrame}" ToolTip="{DynamicResource Str_KeepLastFrameTooltip}"/> <Label Grid.Row="12" Grid.Column="0" Content="{DynamicResource Str_KeepLastFrame}" ToolTip="{DynamicResource Str_KeepLastFrameTooltip}"/>
<ToggleButton Grid.Row="11" Grid.Column="1" IsChecked="{Binding KeepLast}" ToolTip="{DynamicResource Str_KeelLastFrameTooltip}"/> <ToggleButton Grid.Row="12" Grid.Column="1" IsChecked="{Binding KeepLast}" ToolTip="{DynamicResource Str_KeelLastFrameTooltip}"/>
</Grid> </Grid>
</ScrollViewer> </ScrollViewer>
</Border> </Border>

View File

@@ -693,6 +693,7 @@
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<!-- 水平分辨率 --> <!-- 水平分辨率 -->
@@ -731,13 +732,17 @@
<Label Grid.Row="8" Grid.Column="0" Content="{DynamicResource Str_MaxFps}"/> <Label Grid.Row="8" Grid.Column="0" Content="{DynamicResource Str_MaxFps}"/>
<TextBox Grid.Row="8" Grid.Column="1" Text="{Binding MaxFps}"/> <TextBox Grid.Row="8" Grid.Column="1" Text="{Binding MaxFps}"/>
<!-- 播放速度 -->
<Label Grid.Row="9" Grid.Column="0" Content="{DynamicResource Str_PlaySpeed}"/>
<TextBox Grid.Row="9" Grid.Column="1" Text="{Binding Speed}"/>
<!-- 显示坐标轴 --> <!-- 显示坐标轴 -->
<Label Grid.Row="9" Grid.Column="0" Content="{DynamicResource Str_ShowAxis}"/> <Label Grid.Row="10" Grid.Column="0" Content="{DynamicResource Str_ShowAxis}"/>
<ToggleButton Grid.Row="9" Grid.Column="1" IsChecked="{Binding ShowAxis}"/> <ToggleButton Grid.Row="10" Grid.Column="1" IsChecked="{Binding ShowAxis}"/>
<!-- 背景颜色 --> <!-- 背景颜色 -->
<Label Grid.Row="10" Grid.Column="0" Content="{DynamicResource Str_BackgroundColor}" ToolTip="#AARRGGBB"/> <Label Grid.Row="11" Grid.Column="0" Content="{DynamicResource Str_BackgroundColor}" ToolTip="#AARRGGBB"/>
<TextBox Grid.Row="10" Grid.Column="1" Text="{Binding BackgroundColor}" ToolTip="#AARRGGBB"/> <TextBox Grid.Row="11" Grid.Column="1" Text="{Binding BackgroundColor}" ToolTip="#AARRGGBB"/>
<!-- 背景图案 --> <!-- 背景图案 -->
<!-- 背景图案模式 --> <!-- 背景图案模式 -->