Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e717eab6df | ||
|
|
068734549c | ||
|
|
3d1fa38eb3 | ||
|
|
bff3b39371 | ||
|
|
a44161053b | ||
|
|
4b64ec74c2 | ||
|
|
1f56e2f03c | ||
|
|
311b09cc63 | ||
|
|
cd7f841e38 | ||
|
|
df798b481d | ||
|
|
578a9ad3f3 | ||
|
|
b765b5f7ea | ||
|
|
f1c013bd82 | ||
|
|
65c1012205 | ||
|
|
f1cd9e25e5 | ||
|
|
b2861ffb93 | ||
|
|
b01d112d63 | ||
|
|
58b13d00c1 | ||
|
|
0f5539ad41 | ||
|
|
6d18ce882c | ||
|
|
e1ea95c195 | ||
|
|
48ee61d1c6 | ||
|
|
3ca22c3f00 | ||
|
|
593d9b771c | ||
|
|
35f9357355 | ||
|
|
427d18df4c | ||
|
|
7d1a1f1aeb |
26
CHANGELOG.md
26
CHANGELOG.md
@@ -1,5 +1,31 @@
|
|||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
|
||||||
|
## v0.15.5
|
||||||
|
|
||||||
|
- 修复自定义导出时的画面错误
|
||||||
|
- 设置 mp4 像素格式为 yuv420p 避免 windows 默认播放器无法打开
|
||||||
|
- 增加预览画面和导出时的速度参数设置
|
||||||
|
- 修复一些提示文本错误
|
||||||
|
- 导出时自动将分辨率向下调整为 2 的倍数, 避免 yuv420p 格式出错
|
||||||
|
|
||||||
|
## v0.15.4
|
||||||
|
|
||||||
|
- 修复导出时可能的卡死问题
|
||||||
|
- 增加 webp 格式无损压缩参数
|
||||||
|
|
||||||
|
## v0.15.3
|
||||||
|
|
||||||
|
- 增加 skel.bytes 后缀识别
|
||||||
|
|
||||||
|
## v0.15.2
|
||||||
|
|
||||||
|
- 修复首选项文件读取为空时的提示信息
|
||||||
|
- 工作区参数增加浏览路径
|
||||||
|
|
||||||
|
## v0.15.1
|
||||||
|
|
||||||
|
- 新版本正式发布
|
||||||
|
|
||||||
## v0.15.0
|
## v0.15.0
|
||||||
|
|
||||||
### 项目分支变更
|
### 项目分支变更
|
||||||
|
|||||||
@@ -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.1</Version>
|
<Version>0.15.4</Version>
|
||||||
<UseWPF>true</UseWPF>
|
<UseWPF>true</UseWPF>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -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.1</Version>
|
<Version>0.15.4</Version>
|
||||||
<UseWPF>true</UseWPF>
|
<UseWPF>true</UseWPF>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -51,6 +51,12 @@ namespace Spine.Exporters
|
|||||||
public int Quality { get => _quality; set => _quality = Math.Clamp(value, 0, 100); }
|
public int Quality { get => _quality; set => _quality = Math.Clamp(value, 0, 100); }
|
||||||
private int _quality = 75;
|
private int _quality = 75;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 无损压缩 (Webp)
|
||||||
|
/// </summary>
|
||||||
|
public bool Lossless { get => _lossless; set => _lossless = value; }
|
||||||
|
private bool _lossless = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// CRF
|
/// CRF
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -62,7 +68,8 @@ namespace Spine.Exporters
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected override SFMLImageVideoFrame GetFrame(SpineObject[] spines)
|
protected override SFMLImageVideoFrame GetFrame(SpineObject[] spines)
|
||||||
{
|
{
|
||||||
// XXX: 不知道为什么用 FFmpeg 必须临时创建 RenderTexture, 否则无法正常渲染
|
// BUG: 也许和 SFML 多线程或者 FFmpeg 调用有关, 当渲染线程也在运行的时候此处并行渲染会导致和 SFML 有关的内容都卡死
|
||||||
|
// 不知道为什么用 FFmpeg 必须临时创建 RenderTexture, 否则无法正常渲染, 会导致画面帧丢失
|
||||||
using var tex = new RenderTexture(_renderTexture.Size.X, _renderTexture.Size.Y);
|
using var tex = new RenderTexture(_renderTexture.Size.X, _renderTexture.Size.Y);
|
||||||
using var view = _renderTexture.GetView();
|
using var view = _renderTexture.GetView();
|
||||||
tex.SetView(view);
|
tex.SetView(view);
|
||||||
@@ -112,15 +119,17 @@ namespace Spine.Exporters
|
|||||||
|
|
||||||
private void SetWebpOptions(FFMpegArgumentOptions options)
|
private void SetWebpOptions(FFMpegArgumentOptions options)
|
||||||
{
|
{
|
||||||
var customArgs = $"-vf unpremultiply=inplace=1 -quality {_quality} -loop {(_loop ? 0 : 1)}";
|
var customArgs = $"-vf unpremultiply=inplace=1 -quality {_quality} -loop {(_loop ? 0 : 1)} -lossless {(_lossless ? 1 : 0)}";
|
||||||
options.ForceFormat("webp").WithVideoCodec("libwebp_anim").ForcePixelFormat("yuva420p")
|
options.ForceFormat("webp").WithVideoCodec("libwebp_anim").ForcePixelFormat("yuva420p")
|
||||||
.WithCustomArgument(customArgs);
|
.WithCustomArgument(customArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.1</Version>
|
<Version>0.15.5</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ namespace Spine
|
|||||||
public static readonly FrozenDictionary<string, string> PossibleSuffixMapping = new Dictionary<string, string>()
|
public static readonly FrozenDictionary<string, string> PossibleSuffixMapping = new Dictionary<string, string>()
|
||||||
{
|
{
|
||||||
[".skel"] = ".atlas",
|
[".skel"] = ".atlas",
|
||||||
|
[".skel.bytes"] = ".atlas.txt",
|
||||||
[".json"] = ".atlas",
|
[".json"] = ".atlas",
|
||||||
}.ToFrozenDictionary();
|
}.ToFrozenDictionary();
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ namespace SpineViewer.Models
|
|||||||
{
|
{
|
||||||
public class WorkspaceModel
|
public class WorkspaceModel
|
||||||
{
|
{
|
||||||
|
public string? ExploringDirectory { get; set; }
|
||||||
public RendererWorkspaceConfigModel RendererConfig { get; set; } = new();
|
public RendererWorkspaceConfigModel RendererConfig { get; set; } = new();
|
||||||
public List<SpineObjectWorkspaceConfigModel> LoadedSpineObjects { get; set; } = [];
|
public List<SpineObjectWorkspaceConfigModel> LoadedSpineObjects { get; set; } = [];
|
||||||
}
|
}
|
||||||
@@ -36,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; }
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
@@ -174,6 +177,8 @@
|
|||||||
<s:String x:Key="Str_LoopPlayTooltip">Loop animation; only effective for GIF/WebP formats</s:String>
|
<s:String x:Key="Str_LoopPlayTooltip">Loop animation; only effective for GIF/WebP formats</s:String>
|
||||||
<s:String x:Key="Str_QualityParameter">Quality Parameter</s:String>
|
<s:String x:Key="Str_QualityParameter">Quality Parameter</s:String>
|
||||||
<s:String x:Key="Str_QualityParameterTooltip">Range 0–100; higher is better; only for WebP format</s:String>
|
<s:String x:Key="Str_QualityParameterTooltip">Range 0–100; higher is better; only for WebP format</s:String>
|
||||||
|
<s:String x:Key="Str_LosslessParam">Lossless Compression</s:String>
|
||||||
|
<s:String x:Key="Str_LosslessParamTooltip">Lossless compression. Ignores the quality parameter and only applies to WebP format.</s:String>
|
||||||
<s:String x:Key="Str_CrfParameter">CRF Parameter</s:String>
|
<s:String x:Key="Str_CrfParameter">CRF Parameter</s:String>
|
||||||
<s:String x:Key="Str_CrfParameterTooltip">Range 0–63; lower is higher quality; only for MP4/WebM/MKV formats</s:String>
|
<s:String x:Key="Str_CrfParameterTooltip">Range 0–63; lower is higher quality; only for MP4/WebM/MKV formats</s:String>
|
||||||
|
|
||||||
@@ -182,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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
@@ -174,6 +177,8 @@
|
|||||||
<s:String x:Key="Str_LoopPlayTooltip">アニメーションをループ再生するか。Gif/Webp形式のみ有効です</s:String>
|
<s:String x:Key="Str_LoopPlayTooltip">アニメーションをループ再生するか。Gif/Webp形式のみ有効です</s:String>
|
||||||
<s:String x:Key="Str_QualityParameter">品質パラメーター</s:String>
|
<s:String x:Key="Str_QualityParameter">品質パラメーター</s:String>
|
||||||
<s:String x:Key="Str_QualityParameterTooltip">品質パラメーター。値の範囲は0-100。数値が大きいほど品質が高くなります。Webp形式のみ有効です</s:String>
|
<s:String x:Key="Str_QualityParameterTooltip">品質パラメーター。値の範囲は0-100。数値が大きいほど品質が高くなります。Webp形式のみ有効です</s:String>
|
||||||
|
<s:String x:Key="Str_LosslessParam">可逆圧縮</s:String>
|
||||||
|
<s:String x:Key="Str_LosslessParamTooltip">可逆圧縮を行います。品質パラメータは無視され、WebP形式にのみ適用されます。</s:String>
|
||||||
<s:String x:Key="Str_CrfParameter">CRFパラメーター</s:String>
|
<s:String x:Key="Str_CrfParameter">CRFパラメーター</s:String>
|
||||||
<s:String x:Key="Str_CrfParameterTooltip">CRFパラメーター。値の範囲は0-63。数値が小さいほど品質が高くなります。Mp4/Webm/Mkv形式のみ有効です</s:String>
|
<s:String x:Key="Str_CrfParameterTooltip">CRFパラメーター。値の範囲は0-63。数値が小さいほど品質が高くなります。Mp4/Webm/Mkv形式のみ有効です</s:String>
|
||||||
|
|
||||||
@@ -182,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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
@@ -173,7 +176,9 @@
|
|||||||
<s:String x:Key="Str_LoopPlay">循环播放</s:String>
|
<s:String x:Key="Str_LoopPlay">循环播放</s:String>
|
||||||
<s:String x:Key="Str_LoopPlayTooltip">动图是否循环播放,仅对 Gif/Webp 格式生效</s:String>
|
<s:String x:Key="Str_LoopPlayTooltip">动图是否循环播放,仅对 Gif/Webp 格式生效</s:String>
|
||||||
<s:String x:Key="Str_QualityParameter">质量参数</s:String>
|
<s:String x:Key="Str_QualityParameter">质量参数</s:String>
|
||||||
<s:String x:Key="Str_QualityParameterTooltip">质量参数,取值范围 0-100,越高质量越好 仅对 Webp 格式生效</s:String>
|
<s:String x:Key="Str_QualityParameterTooltip">质量参数,取值范围 0-100,越高质量越好, 仅对 Webp 格式生效</s:String>
|
||||||
|
<s:String x:Key="Str_LosslessParam">无损压缩</s:String>
|
||||||
|
<s:String x:Key="Str_LosslessParamTooltip">无损压缩, 会忽略质量参数, 仅对 Webp 格式生效</s:String>
|
||||||
<s:String x:Key="Str_CrfParameter">CRF 参数</s:String>
|
<s:String x:Key="Str_CrfParameter">CRF 参数</s:String>
|
||||||
<s:String x:Key="Str_CrfParameterTooltip">CRF 参数,取值范围 0-63,越小质量越高,仅对 Mp4/Webm/Mkv 格式生效</s:String>
|
<s:String x:Key="Str_CrfParameterTooltip">CRF 参数,取值范围 0-63,越小质量越高,仅对 Mp4/Webm/Mkv 格式生效</s:String>
|
||||||
|
|
||||||
@@ -182,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>
|
||||||
|
|||||||
@@ -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.1</Version>
|
<Version>0.15.5</Version>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<UseWPF>true</UseWPF>
|
<UseWPF>true</UseWPF>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|||||||
@@ -40,13 +40,16 @@ namespace SpineViewer.Utils
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 从文件反序列对象, 不会抛出异常
|
/// 从文件反序列对象, 不会抛出异常
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool Deserialize<T>(string path, out T obj)
|
public static bool Deserialize<T>(string path, out T obj, bool quietForNotExist = false)
|
||||||
{
|
{
|
||||||
if (!File.Exists(path))
|
if (!File.Exists(path))
|
||||||
|
{
|
||||||
|
if (!quietForNotExist)
|
||||||
{
|
{
|
||||||
_logger.Error("Json file {0} not found", path);
|
_logger.Error("Json file {0} not found", path);
|
||||||
MessagePopupService.Error($"Json file {path} not found");
|
MessagePopupService.Error($"Json file {path} not found");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -142,7 +142,6 @@ namespace SpineViewer.ViewModels.Exporters
|
|||||||
{
|
{
|
||||||
if (!Export_CanExecute(args)) return;
|
if (!Export_CanExecute(args)) return;
|
||||||
Export(args.Cast<SpineObjectModel>().ToArray());
|
Export(args.Cast<SpineObjectModel>().ToArray());
|
||||||
// XXX: 导出途中应该停掉渲染好一些, 让性能专注在导出上
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool Export_CanExecute(IList? args)
|
private bool Export_CanExecute(IList? args)
|
||||||
|
|||||||
@@ -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,
|
||||||
@@ -83,6 +84,8 @@ namespace SpineViewer.ViewModels.Exporters
|
|||||||
exporter.Rotation = view.Rotation;
|
exporter.Rotation = view.Rotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_vmMain.SFMLRendererViewModel.StopRender();
|
||||||
|
|
||||||
if (_exportSingle)
|
if (_exportSingle)
|
||||||
{
|
{
|
||||||
var filename = $"ffmpeg_{timestamp}_{Guid.NewGuid().ToString()[..6]}_{_fps}{FormatSuffix}";
|
var filename = $"ffmpeg_{timestamp}_{Guid.NewGuid().ToString()[..6]}_{_fps}{FormatSuffix}";
|
||||||
@@ -168,6 +171,8 @@ namespace SpineViewer.ViewModels.Exporters
|
|||||||
}
|
}
|
||||||
_vmMain.ProgressState = System.Windows.Shell.TaskbarItemProgressState.None;
|
_vmMain.ProgressState = System.Windows.Shell.TaskbarItemProgressState.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_vmMain.SFMLRendererViewModel.StartRender();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ namespace SpineViewer.ViewModels.Exporters
|
|||||||
public int Quality { get => _quality; set => SetProperty(ref _quality, Math.Clamp(value, 0, 100)); }
|
public int Quality { get => _quality; set => SetProperty(ref _quality, Math.Clamp(value, 0, 100)); }
|
||||||
protected int _quality = 75;
|
protected int _quality = 75;
|
||||||
|
|
||||||
|
public bool Lossless { get => _lossless; set => SetProperty(ref _lossless, value); }
|
||||||
|
protected bool _lossless = false;
|
||||||
|
|
||||||
public int Crf { get => _crf; set => SetProperty(ref _crf, Math.Clamp(value, 0, 63)); }
|
public int Crf { get => _crf; set => SetProperty(ref _crf, Math.Clamp(value, 0, 63)); }
|
||||||
protected int _crf = 23;
|
protected int _crf = 23;
|
||||||
|
|
||||||
@@ -51,10 +54,12 @@ 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,
|
||||||
Quality = _quality,
|
Quality = _quality,
|
||||||
|
Lossless = _lossless,
|
||||||
Crf = _crf
|
Crf = _crf
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -68,6 +73,10 @@ namespace SpineViewer.ViewModels.Exporters
|
|||||||
exporter.Rotation = view.Rotation;
|
exporter.Rotation = view.Rotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BUG: FFmpeg 导出时对 RenderTexture 的频繁资源申请释放似乎使 SFML 库内部出现问题, 会卡死所有使用 SFML 的地方, 包括渲染线程
|
||||||
|
// 所以临时把渲染线程停掉, 只让此处使用 SFML 资源, 这个问题或许和多个线程同时使用渲染资源有关
|
||||||
|
_vmMain.SFMLRendererViewModel.StopRender();
|
||||||
|
|
||||||
if (_exportSingle)
|
if (_exportSingle)
|
||||||
{
|
{
|
||||||
var filename = $"video_{timestamp}_{Guid.NewGuid().ToString()[..6]}_{_fps}{FormatSuffix}";
|
var filename = $"video_{timestamp}_{Guid.NewGuid().ToString()[..6]}_{_fps}{FormatSuffix}";
|
||||||
@@ -153,6 +162,8 @@ namespace SpineViewer.ViewModels.Exporters
|
|||||||
}
|
}
|
||||||
_vmMain.ProgressState = System.Windows.Shell.TaskbarItemProgressState.None;
|
_vmMain.ProgressState = System.Windows.Shell.TaskbarItemProgressState.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_vmMain.SFMLRendererViewModel.StartRender();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -47,6 +48,8 @@ namespace SpineViewer.ViewModels.Exporters
|
|||||||
exporter.Rotation = view.Rotation;
|
exporter.Rotation = view.Rotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_vmMain.SFMLRendererViewModel.StopRender();
|
||||||
|
|
||||||
if (_exportSingle)
|
if (_exportSingle)
|
||||||
{
|
{
|
||||||
var folderName = $"frames_{timestamp}_{Guid.NewGuid().ToString()[..6]}_{_fps}";
|
var folderName = $"frames_{timestamp}_{Guid.NewGuid().ToString()[..6]}_{_fps}";
|
||||||
@@ -132,6 +135,8 @@ namespace SpineViewer.ViewModels.Exporters
|
|||||||
}
|
}
|
||||||
_vmMain.ProgressState = System.Windows.Shell.TaskbarItemProgressState.None;
|
_vmMain.ProgressState = System.Windows.Shell.TaskbarItemProgressState.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_vmMain.SFMLRendererViewModel.StartRender();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,11 +39,6 @@ namespace SpineViewer.ViewModels.MainWindow
|
|||||||
|
|
||||||
private readonly MainWindowViewModel _vmMain;
|
private readonly MainWindowViewModel _vmMain;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 当前目录路径
|
|
||||||
/// </summary>
|
|
||||||
private string? _currentDirectory;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 当前目录下文件项缓存
|
/// 当前目录下文件项缓存
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -54,12 +49,26 @@ namespace SpineViewer.ViewModels.MainWindow
|
|||||||
_vmMain = vmMain;
|
_vmMain = vmMain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前目录路径
|
||||||
|
/// </summary>
|
||||||
|
public string? CurrentDirectory
|
||||||
|
{
|
||||||
|
get => string.IsNullOrWhiteSpace(_currentDirectory) ? null : _currentDirectory;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (!SetProperty(ref _currentDirectory, value)) return;
|
||||||
|
RefreshItems();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private string? _currentDirectory;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 筛选字符串
|
/// 筛选字符串
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? FilterString
|
public string? FilterString
|
||||||
{
|
{
|
||||||
get => _filterString;
|
get => string.IsNullOrWhiteSpace(_filterString) ? null : _filterString;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (!SetProperty(ref _filterString, value)) return;
|
if (!SetProperty(ref _filterString, value)) return;
|
||||||
@@ -95,10 +104,7 @@ namespace SpineViewer.ViewModels.MainWindow
|
|||||||
public RelayCommand Cmd_ChangeCurrentDirectory => _cmd_ChangeCurrentDirectory ??= new(() =>
|
public RelayCommand Cmd_ChangeCurrentDirectory => _cmd_ChangeCurrentDirectory ??= new(() =>
|
||||||
{
|
{
|
||||||
if (DialogService.ShowOpenFolderDialog(out var selectedPath))
|
if (DialogService.ShowOpenFolderDialog(out var selectedPath))
|
||||||
{
|
CurrentDirectory = selectedPath;
|
||||||
_currentDirectory = selectedPath;
|
|
||||||
RefreshItems();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
private RelayCommand? _cmd_ChangeCurrentDirectory;
|
private RelayCommand? _cmd_ChangeCurrentDirectory;
|
||||||
|
|
||||||
|
|||||||
@@ -118,12 +118,14 @@ namespace SpineViewer.ViewModels.MainWindow
|
|||||||
{
|
{
|
||||||
return new()
|
return new()
|
||||||
{
|
{
|
||||||
|
ExploringDirectory = _explorerListViewModel.CurrentDirectory,
|
||||||
RendererConfig = _sfmlRendererViewModel.WorkspaceConfig,
|
RendererConfig = _sfmlRendererViewModel.WorkspaceConfig,
|
||||||
LoadedSpineObjects = _spineObjectListViewModel.LoadedSpineObjects
|
LoadedSpineObjects = _spineObjectListViewModel.LoadedSpineObjects
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
_explorerListViewModel.CurrentDirectory = value.ExploringDirectory;
|
||||||
_sfmlRendererViewModel.WorkspaceConfig = value.RendererConfig;
|
_sfmlRendererViewModel.WorkspaceConfig = value.RendererConfig;
|
||||||
_spineObjectListViewModel.LoadedSpineObjects = value.LoadedSpineObjects;
|
_spineObjectListViewModel.LoadedSpineObjects = value.LoadedSpineObjects;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ namespace SpineViewer.ViewModels.MainWindow
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void LoadPreference()
|
public void LoadPreference()
|
||||||
{
|
{
|
||||||
if (JsonHelper.Deserialize<PreferenceModel>(PreferenceFilePath, out var obj))
|
if (JsonHelper.Deserialize<PreferenceModel>(PreferenceFilePath, out var obj, true))
|
||||||
Preference = obj;
|
Preference = obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -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)
|
||||||
@@ -441,6 +448,7 @@ 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,
|
||||||
};
|
};
|
||||||
@@ -456,6 +464,7 @@ namespace SpineViewer.ViewModels.MainWindow
|
|||||||
FlipX = value.FlipX;
|
FlipX = value.FlipX;
|
||||||
FlipY = value.FlipY;
|
FlipY = value.FlipY;
|
||||||
MaxFps = value.MaxFps;
|
MaxFps = value.MaxFps;
|
||||||
|
Speed = value.Speed;
|
||||||
ShowAxis = value.ShowAxis;
|
ShowAxis = value.ShowAxis;
|
||||||
BackgroundColor = value.BackgroundColor;
|
BackgroundColor = value.BackgroundColor;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ namespace SpineViewer.ViewModels.MainWindow
|
|||||||
|
|
||||||
private void AddSpineObject_Execute()
|
private void AddSpineObject_Execute()
|
||||||
{
|
{
|
||||||
MessagePopupService.Info("Not Implemented, try next version :)");
|
MessagePopupService.Info("Not Implemented, please drag files into here or add them from clipboard :)");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Title="{DynamicResource Str_FFmpegVideoExporterTitle}"
|
Title="{DynamicResource Str_FFmpegVideoExporterTitle}"
|
||||||
Width="450"
|
Width="450"
|
||||||
Height="550"
|
Height="580"
|
||||||
ShowInTaskbar="False"
|
ShowInTaskbar="False"
|
||||||
WindowStartupLocation="CenterOwner">
|
WindowStartupLocation="CenterOwner">
|
||||||
<DockPanel>
|
<DockPanel>
|
||||||
@@ -66,6 +66,8 @@
|
|||||||
<RowDefinition Height="Auto"/>
|
<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>
|
||||||
|
|
||||||
<!-- 水平分辨率 -->
|
<!-- 水平分辨率 -->
|
||||||
@@ -120,27 +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="17" Grid.Column="0" Content="{DynamicResource Str_LosslessParam}" ToolTip="{DynamicResource Str_LosslessParamTooltip}"/>
|
||||||
|
<ToggleButton Grid.Row="17" Grid.Column="1" IsChecked="{Binding Lossless}" ToolTip="{DynamicResource Str_LosslessParamTooltip}"/>
|
||||||
|
|
||||||
<!-- CRF 参数 -->
|
<!-- CRF 参数 -->
|
||||||
<Label Grid.Row="16" 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="16" 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>
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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"/>
|
||||||
|
|
||||||
<!-- 背景图案 -->
|
<!-- 背景图案 -->
|
||||||
<!-- 背景图案模式 -->
|
<!-- 背景图案模式 -->
|
||||||
|
|||||||
Reference in New Issue
Block a user