diff --git a/Spine/Exporters/FFmpegVideoExporter.cs b/Spine/Exporters/FFmpegVideoExporter.cs
index e3fd2c3..ea04666 100644
--- a/Spine/Exporters/FFmpegVideoExporter.cs
+++ b/Spine/Exporters/FFmpegVideoExporter.cs
@@ -28,6 +28,7 @@ namespace Spine.Exporters
{
Gif,
Webp,
+ Apng,
Mp4,
Webm,
Mkv,
@@ -41,31 +42,37 @@ namespace Spine.Exporters
private VideoFormat _format = VideoFormat.Mp4;
///
- /// 动图是否循环 [Gif/Webp]
+ /// [Gif/Webp/Apng] 动图是否循环
///
public bool Loop { get => _loop; set => _loop = value; }
private bool _loop = true;
///
- /// 质量 [Webp]
+ /// [Webp] 质量
///
public int Quality { get => _quality; set => _quality = Math.Clamp(value, 0, 100); }
private int _quality = 75;
///
- /// 无损压缩 [Webp]
+ /// [Webp] 无损压缩
///
public bool Lossless { get => _lossless; set => _lossless = value; }
private bool _lossless = false;
///
- /// CRF [Mp4/Webm/Mkv]
+ /// [Apng] 预测器算法, 取值范围 0-5, 分别对应 none, sub, up, avg, paeth, mixed
+ ///
+ public int ApngPred { get => _apngPred; set => _apngPred = Math.Clamp(value, 0, 5); }
+ private int _apngPred = 5;
+
+ ///
+ /// [Mp4/Webm/Mkv] CRF
///
public int Crf { get => _crf; set => _crf = Math.Clamp(value, 0, 63); }
private int _crf = 23;
///
- /// prores_ks 编码器的配置等级, -1 是自动, 越高质量越好, 只有 4 及以上才有透明通道 [Mov]
+ /// [Mov] prores_ks 编码器的配置等级, -1 是自动, 越高质量越好, 只有 4 及以上才有透明通道
///
public int Profile { get => _profile; set => _profile = Math.Clamp(value, -1, 5); }
private int _profile = 5;
@@ -93,6 +100,7 @@ namespace Spine.Exporters
{
VideoFormat.Gif => SetGifOptions,
VideoFormat.Webp => SetWebpOptions,
+ VideoFormat.Apng => SetApngOptions,
VideoFormat.Mp4 => SetMp4Options,
VideoFormat.Webm => SetWebmOptions,
VideoFormat.Mkv => SetMkvOptions,
@@ -132,6 +140,13 @@ namespace Spine.Exporters
.WithCustomArgument(customArgs);
}
+ private void SetApngOptions(FFMpegArgumentOptions options)
+ {
+ var customArgs = $"-vf unpremultiply=inplace=1 -plays {(_loop ? 0 : 1)} -pred {_apngPred}";
+ options.ForceFormat("apng").WithVideoCodec("apng").ForcePixelFormat("rgba")
+ .WithCustomArgument(customArgs);
+ }
+
private void SetMp4Options(FFMpegArgumentOptions options)
{
// XXX: windows 默认播放器在播放 MP4 格式时对于 libx264 编码器只支持 yuv420p 的像素格式
diff --git a/SpineViewer/Resources/Strings/en.xaml b/SpineViewer/Resources/Strings/en.xaml
index be3fd86..0ddc1af 100644
--- a/SpineViewer/Resources/Strings/en.xaml
+++ b/SpineViewer/Resources/Strings/en.xaml
@@ -197,11 +197,13 @@
Video Format
Loop Play
- [Gif/Webp]
Whether the animation loops
+ [Gif/Webp/Apng]
Whether the animation loops
Quality Parameter
[Webp]
Quality parameter, range 0-100, higher value means better quality
Lossless Compression
[Webp]
Lossless compression, quality parameter will be ignored
+ Predictor Method
+ [Apng]
Pred parameter, value range 0-5, corresponding to different encoding strategies: none, sub, up, avg, paeth, and mixed.
It affects encoding time and file size.
CRF Parameter
[Mp4/Webm/Mkv]
CRF parameter, range 0-63, lower value means higher quality
Profile Parameter
diff --git a/SpineViewer/Resources/Strings/ja.xaml b/SpineViewer/Resources/Strings/ja.xaml
index 6790f47..39ce6bc 100644
--- a/SpineViewer/Resources/Strings/ja.xaml
+++ b/SpineViewer/Resources/Strings/ja.xaml
@@ -197,11 +197,13 @@
ビデオフォーマット
ループ再生
- [Gif/Webp]
アニメーションをループ再生するかどうか
+ [Gif/Webp/Apng]
アニメーションをループ再生するかどうか
品質パラメータ
[Webp]
品質パラメータ、範囲は0-100。値が高いほど品質が良い
無損失圧縮
[Webp]
無損失圧縮、品質パラメータは無視されます
+ 予測器方式
+ [Apng]
Pred パラメータ。値の範囲は 0~5 で、それぞれ none、sub、up、avg、paeth、mixed の異なるエンコード方式に対応します。
エンコード時間とファイルサイズに影響します。
CRF パラメータ
[Mp4/Webm/Mkv]
CRF パラメータ、範囲0-63。値が小さいほど品質が高い
プロファイルパラメータ
diff --git a/SpineViewer/Resources/Strings/zh.xaml b/SpineViewer/Resources/Strings/zh.xaml
index 200e717..9eef84f 100644
--- a/SpineViewer/Resources/Strings/zh.xaml
+++ b/SpineViewer/Resources/Strings/zh.xaml
@@ -197,11 +197,13 @@
视频格式
循环播放
- [Gif/Webp]
动图是否循环播放
+ [Gif/Webp/Apng]
动图是否循环播放
质量参数
[Webp]
质量参数,取值范围 0-100,越高质量越好
无损压缩
[Webp]
无损压缩,会忽略质量参数
+ 预测器方法
+ [Apng]
Pred 参数,取值范围 0-5,分别对应 none、sub、up、avg、paeth、mixed 几种不同的编码策略,
影响编码时间和文件大小
CRF 参数
[Mp4/Webm/Mkv]
CRF 参数,取值范围 0-63,越小质量越高
Profile 参数
diff --git a/SpineViewer/ViewModels/Exporters/FFmpegVideoExporterViewModel.cs b/SpineViewer/ViewModels/Exporters/FFmpegVideoExporterViewModel.cs
index ed9d098..c11b986 100644
--- a/SpineViewer/ViewModels/Exporters/FFmpegVideoExporterViewModel.cs
+++ b/SpineViewer/ViewModels/Exporters/FFmpegVideoExporterViewModel.cs
@@ -20,24 +20,64 @@ namespace SpineViewer.ViewModels.Exporters
{
public static ImmutableArray VideoFormatOptions { get; } = Enum.GetValues().ToImmutableArray();
- public FFmpegVideoExporter.VideoFormat Format { get => _format; set => SetProperty(ref _format, value); }
+ public FFmpegVideoExporter.VideoFormat Format
+ {
+ get => _format;
+ set
+ {
+ if (SetProperty(ref _format, value))
+ {
+ OnPropertyChanged(nameof(EnableParamLoop));
+ OnPropertyChanged(nameof(EnableParamQuality));
+ OnPropertyChanged(nameof(EnableParamLossless));
+ OnPropertyChanged(nameof(EnableParamApngPred));
+ OnPropertyChanged(nameof(EnableParamCrf));
+ OnPropertyChanged(nameof(EnableParamProfile));
+ }
+ }
+ }
protected FFmpegVideoExporter.VideoFormat _format = FFmpegVideoExporter.VideoFormat.Mp4;
public bool Loop { get => _loop; set => SetProperty(ref _loop, value); }
protected bool _loop = true;
+ public bool EnableParamLoop =>
+ _format == FFmpegVideoExporter.VideoFormat.Gif ||
+ _format == FFmpegVideoExporter.VideoFormat.Webp ||
+ _format == FFmpegVideoExporter.VideoFormat.Apng;
+
public int Quality { get => _quality; set => SetProperty(ref _quality, Math.Clamp(value, 0, 100)); }
protected int _quality = 75;
+ public bool EnableParamQuality =>
+ _format == FFmpegVideoExporter.VideoFormat.Webp;
+
public bool Lossless { get => _lossless; set => SetProperty(ref _lossless, value); }
protected bool _lossless = false;
+ public bool EnableParamLossless =>
+ _format == FFmpegVideoExporter.VideoFormat.Webp;
+
+ public int ApngPred { get => _apngPred; set => SetProperty(ref _apngPred, Math.Clamp(value, 0, 5)); }
+ protected int _apngPred = 5;
+
+ public bool EnableParamApngPred =>
+ _format == FFmpegVideoExporter.VideoFormat.Apng;
+
public int Crf { get => _crf; set => SetProperty(ref _crf, Math.Clamp(value, 0, 63)); }
protected int _crf = 23;
+ public bool EnableParamCrf =>
+ _format == FFmpegVideoExporter.VideoFormat.Mp4 ||
+ _format == FFmpegVideoExporter.VideoFormat.Webm ||
+ _format == FFmpegVideoExporter.VideoFormat.Mkv;
+
public int Profile { get => _profile; set => SetProperty(ref _profile, Math.Clamp(value, -1, 5)); }
protected int _profile = 5;
+ public bool EnableParamProfile =>
+ _format == FFmpegVideoExporter.VideoFormat.Mov;
+
private string FormatSuffix => $".{_format.ToString().ToLower()}";
protected override void Export(SpineObjectModel[] models)
@@ -63,6 +103,7 @@ namespace SpineViewer.ViewModels.Exporters
Loop = _loop,
Quality = _quality,
Lossless = _lossless,
+ ApngPred = _apngPred,
Crf = _crf,
Profile = _profile,
};
diff --git a/SpineViewer/Views/AboutDialog.xaml b/SpineViewer/Views/AboutDialog.xaml
index 7fa3eac..3be6fbb 100644
--- a/SpineViewer/Views/AboutDialog.xaml
+++ b/SpineViewer/Views/AboutDialog.xaml
@@ -26,25 +26,31 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
diff --git a/SpineViewer/Views/DiagnosticsDialog.xaml b/SpineViewer/Views/DiagnosticsDialog.xaml
index a4f0f75..6ba4427 100644
--- a/SpineViewer/Views/DiagnosticsDialog.xaml
+++ b/SpineViewer/Views/DiagnosticsDialog.xaml
@@ -21,7 +21,7 @@
-
+
-
-
+
+
@@ -134,7 +134,6 @@
-
diff --git a/SpineViewer/Views/ExporterDialogs/CustomFFmpegExporterDialog.xaml b/SpineViewer/Views/ExporterDialogs/CustomFFmpegExporterDialog.xaml
index 2ea46ed..81f9288 100644
--- a/SpineViewer/Views/ExporterDialogs/CustomFFmpegExporterDialog.xaml
+++ b/SpineViewer/Views/ExporterDialogs/CustomFFmpegExporterDialog.xaml
@@ -49,163 +49,219 @@
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/SpineViewer/Views/ExporterDialogs/FFmpegVideoExporterDialog.xaml b/SpineViewer/Views/ExporterDialogs/FFmpegVideoExporterDialog.xaml
index 2969bad..6397d99 100644
--- a/SpineViewer/Views/ExporterDialogs/FFmpegVideoExporterDialog.xaml
+++ b/SpineViewer/Views/ExporterDialogs/FFmpegVideoExporterDialog.xaml
@@ -53,136 +53,216 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/SpineViewer/Views/ExporterDialogs/FrameExporterDialog.xaml b/SpineViewer/Views/ExporterDialogs/FrameExporterDialog.xaml
index ce655f0..0e893d7 100644
--- a/SpineViewer/Views/ExporterDialogs/FrameExporterDialog.xaml
+++ b/SpineViewer/Views/ExporterDialogs/FrameExporterDialog.xaml
@@ -53,85 +53,122 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
-
-
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/SpineViewer/Views/ExporterDialogs/FrameSequenceExporterDialog.xaml b/SpineViewer/Views/ExporterDialogs/FrameSequenceExporterDialog.xaml
index 9362b8c..45c533d 100644
--- a/SpineViewer/Views/ExporterDialogs/FrameSequenceExporterDialog.xaml
+++ b/SpineViewer/Views/ExporterDialogs/FrameSequenceExporterDialog.xaml
@@ -53,95 +53,142 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/SpineViewer/Views/MainWindow.xaml b/SpineViewer/Views/MainWindow.xaml
index 3aeead4..7984959 100644
--- a/SpineViewer/Views/MainWindow.xaml
+++ b/SpineViewer/Views/MainWindow.xaml
@@ -44,6 +44,20 @@
+
+
+
+
+
+
+
+
+
+
+
@@ -225,7 +239,7 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
@@ -932,21 +1107,6 @@
Opened="BottomPopup_Opened"
MouseLeave="PopupContainer_MouseLeave"/>
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/SpineViewer/Views/PreferenceDialog.xaml b/SpineViewer/Views/PreferenceDialog.xaml
index 4721c9c..a1946a3 100644
--- a/SpineViewer/Views/PreferenceDialog.xaml
+++ b/SpineViewer/Views/PreferenceDialog.xaml
@@ -57,7 +57,7 @@
-
+
@@ -66,7 +66,7 @@
-
+
@@ -75,7 +75,7 @@
-
+
@@ -88,7 +88,7 @@
-
+
@@ -97,7 +97,7 @@
-
+
@@ -106,7 +106,7 @@
-
+
@@ -115,7 +115,7 @@
-
+
@@ -124,7 +124,7 @@
-
+
@@ -133,7 +133,7 @@
-
+
@@ -142,7 +142,7 @@
-
+
@@ -151,7 +151,7 @@
-
+
@@ -160,7 +160,7 @@
-
+
@@ -169,7 +169,7 @@