diff --git a/SpineViewer/Controls/SpinePreviewer.cs b/SpineViewer/Controls/SpinePreviewer.cs
index 7e1262d..eacc056 100644
--- a/SpineViewer/Controls/SpinePreviewer.cs
+++ b/SpineViewer/Controls/SpinePreviewer.cs
@@ -9,7 +9,6 @@ using System.Threading.Tasks;
using System.Windows.Forms;
using System.Security.Policy;
using System.Diagnostics;
-using NLog.Targets;
namespace SpineViewer.Controls
{
@@ -246,6 +245,11 @@ namespace SpineViewer.Controls
public uint MaxFps { get => maxFps; set { RenderWindow.SetFramerateLimit(value); maxFps = value; } }
private uint maxFps = 60;
+ ///
+ /// 获取 View
+ ///
+ public SFML.Graphics.View GetView() => RenderWindow.GetView();
+
#endregion
public SpinePreviewer()
@@ -261,11 +265,6 @@ namespace SpineViewer.Controls
MaxFps = 30;
}
- ///
- /// 预览画面帧参数, TODO: 转移到统一导出参数
- ///
- public SpinePreviewerFrameArgs GetFrameArgs() => new(Resolution, RenderWindow.GetView(), RenderSelectedOnly);
-
#region 渲染线程管理
///
@@ -595,26 +594,4 @@ namespace SpineViewer.Controls
PauseUpdate();
}
}
-
- ///
- /// 预览画面帧参数
- ///
- public class SpinePreviewerFrameArgs(Size resolution, SFML.Graphics.View view, bool renderSelectedOnly)
- {
- ///
- /// 分辨率
- ///
- public Size Resolution => resolution;
-
- ///
- /// 渲染视窗
- ///
- public SFML.Graphics.View View => view;
-
- ///
- /// 是否仅渲染/导出选中骨骼
- ///
- public bool RenderSelectedOnly => renderSelectedOnly;
- }
-
}
diff --git a/SpineViewer/Dialogs/ExportPreviewDialog.Designer.cs b/SpineViewer/Dialogs/ExportDialog.Designer.cs
similarity index 54%
rename from SpineViewer/Dialogs/ExportPreviewDialog.Designer.cs
rename to SpineViewer/Dialogs/ExportDialog.Designer.cs
index 0629ded..2568017 100644
--- a/SpineViewer/Dialogs/ExportPreviewDialog.Designer.cs
+++ b/SpineViewer/Dialogs/ExportDialog.Designer.cs
@@ -1,6 +1,6 @@
namespace SpineViewer.Dialogs
{
- partial class ExportPreviewDialog
+ partial class ExportDialog
{
///
/// Required designer variable.
@@ -28,18 +28,13 @@
///
private void InitializeComponent()
{
- System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ExportPreviewDialog));
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ExportDialog));
panel1 = new Panel();
tableLayoutPanel1 = new TableLayoutPanel();
- propertyGrid = new PropertyGrid();
- label4 = new Label();
- label1 = new Label();
- textBox_OutputDir = new TextBox();
- button_SelectOutputDir = new Button();
+ propertyGrid_ExportArgs = new PropertyGrid();
tableLayoutPanel2 = new TableLayoutPanel();
button_Ok = new Button();
button_Cancel = new Button();
- folderBrowserDialog = new FolderBrowserDialog();
panel1.SuspendLayout();
tableLayoutPanel1.SuspendLayout();
tableLayoutPanel2.SuspendLayout();
@@ -52,112 +47,58 @@
panel1.Location = new Point(0, 0);
panel1.Name = "panel1";
panel1.Padding = new Padding(50, 15, 50, 10);
- panel1.Size = new Size(914, 482);
+ panel1.Size = new Size(710, 698);
panel1.TabIndex = 2;
//
// tableLayoutPanel1
//
tableLayoutPanel1.AutoSize = true;
- tableLayoutPanel1.ColumnCount = 4;
- tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle());
- tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F));
- tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F));
- tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle());
- tableLayoutPanel1.Controls.Add(propertyGrid, 0, 2);
- tableLayoutPanel1.Controls.Add(label4, 0, 0);
- tableLayoutPanel1.Controls.Add(label1, 0, 1);
- tableLayoutPanel1.Controls.Add(textBox_OutputDir, 1, 1);
- tableLayoutPanel1.Controls.Add(button_SelectOutputDir, 3, 1);
- tableLayoutPanel1.Controls.Add(tableLayoutPanel2, 0, 3);
+ tableLayoutPanel1.ColumnCount = 1;
+ tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
+ tableLayoutPanel1.Controls.Add(propertyGrid_ExportArgs, 0, 0);
+ tableLayoutPanel1.Controls.Add(tableLayoutPanel2, 0, 1);
tableLayoutPanel1.Dock = DockStyle.Fill;
tableLayoutPanel1.Location = new Point(50, 15);
tableLayoutPanel1.Name = "tableLayoutPanel1";
- tableLayoutPanel1.RowCount = 4;
- tableLayoutPanel1.RowStyles.Add(new RowStyle());
- tableLayoutPanel1.RowStyles.Add(new RowStyle());
+ tableLayoutPanel1.RowCount = 2;
tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
tableLayoutPanel1.RowStyles.Add(new RowStyle());
- tableLayoutPanel1.Size = new Size(814, 457);
+ tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Absolute, 20F));
+ tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Absolute, 20F));
+ tableLayoutPanel1.Size = new Size(610, 673);
tableLayoutPanel1.TabIndex = 0;
//
- // propertyGrid
+ // propertyGrid_ExportArgs
//
- tableLayoutPanel1.SetColumnSpan(propertyGrid, 4);
- propertyGrid.Dock = DockStyle.Fill;
- propertyGrid.HelpVisible = false;
- propertyGrid.Location = new Point(3, 97);
- propertyGrid.Name = "propertyGrid";
- propertyGrid.Size = new Size(808, 284);
- propertyGrid.TabIndex = 1;
- propertyGrid.ToolbarVisible = false;
- //
- // label4
- //
- label4.AutoSize = true;
- tableLayoutPanel1.SetColumnSpan(label4, 4);
- label4.Dock = DockStyle.Fill;
- label4.Location = new Point(15, 15);
- label4.Margin = new Padding(15);
- label4.Name = "label4";
- label4.Size = new Size(784, 24);
- label4.TabIndex = 11;
- label4.Text = "说明:输出文件夹为可选项,留空则将预览图输出到每个骨骼文件所在目录";
- label4.TextAlign = ContentAlignment.MiddleCenter;
- //
- // label1
- //
- label1.Anchor = AnchorStyles.Right;
- label1.AutoSize = true;
- label1.Location = new Point(3, 62);
- label1.Name = "label1";
- label1.Size = new Size(104, 24);
- label1.TabIndex = 0;
- label1.Text = "输出文件夹:";
- //
- // textBox_OutputDir
- //
- tableLayoutPanel1.SetColumnSpan(textBox_OutputDir, 2);
- textBox_OutputDir.Dock = DockStyle.Fill;
- textBox_OutputDir.Location = new Point(113, 57);
- textBox_OutputDir.Name = "textBox_OutputDir";
- textBox_OutputDir.Size = new Size(660, 30);
- textBox_OutputDir.TabIndex = 3;
- //
- // button_SelectOutputDir
- //
- button_SelectOutputDir.AutoSize = true;
- button_SelectOutputDir.AutoSizeMode = AutoSizeMode.GrowAndShrink;
- button_SelectOutputDir.Location = new Point(779, 57);
- button_SelectOutputDir.Name = "button_SelectOutputDir";
- button_SelectOutputDir.Size = new Size(32, 34);
- button_SelectOutputDir.TabIndex = 5;
- button_SelectOutputDir.Text = "...";
- button_SelectOutputDir.UseVisualStyleBackColor = true;
- button_SelectOutputDir.Click += button_SelectOutputDir_Click;
+ propertyGrid_ExportArgs.Dock = DockStyle.Fill;
+ propertyGrid_ExportArgs.Location = new Point(3, 3);
+ propertyGrid_ExportArgs.Name = "propertyGrid_ExportArgs";
+ propertyGrid_ExportArgs.Size = new Size(604, 594);
+ propertyGrid_ExportArgs.TabIndex = 1;
+ propertyGrid_ExportArgs.ToolbarVisible = false;
//
// tableLayoutPanel2
//
tableLayoutPanel2.AutoSize = true;
tableLayoutPanel2.AutoSizeMode = AutoSizeMode.GrowAndShrink;
tableLayoutPanel2.ColumnCount = 2;
- tableLayoutPanel1.SetColumnSpan(tableLayoutPanel2, 4);
tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F));
tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F));
tableLayoutPanel2.Controls.Add(button_Ok, 0, 0);
tableLayoutPanel2.Controls.Add(button_Cancel, 1, 0);
tableLayoutPanel2.Dock = DockStyle.Bottom;
- tableLayoutPanel2.Location = new Point(3, 414);
+ tableLayoutPanel2.Location = new Point(3, 630);
tableLayoutPanel2.Margin = new Padding(3, 30, 3, 3);
tableLayoutPanel2.Name = "tableLayoutPanel2";
tableLayoutPanel2.RowCount = 1;
tableLayoutPanel2.RowStyles.Add(new RowStyle());
- tableLayoutPanel2.Size = new Size(808, 40);
+ tableLayoutPanel2.Size = new Size(604, 40);
tableLayoutPanel2.TabIndex = 10;
//
// button_Ok
//
button_Ok.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
- button_Ok.Location = new Point(262, 3);
+ button_Ok.Location = new Point(160, 3);
button_Ok.Margin = new Padding(3, 3, 30, 3);
button_Ok.Name = "button_Ok";
button_Ok.Size = new Size(112, 34);
@@ -169,7 +110,7 @@
// button_Cancel
//
button_Cancel.Anchor = AnchorStyles.Bottom | AnchorStyles.Left;
- button_Cancel.Location = new Point(434, 3);
+ button_Cancel.Location = new Point(332, 3);
button_Cancel.Margin = new Padding(30, 3, 3, 3);
button_Cancel.Name = "button_Cancel";
button_Cancel.Size = new Size(112, 34);
@@ -178,26 +119,22 @@
button_Cancel.UseVisualStyleBackColor = true;
button_Cancel.Click += button_Cancel_Click;
//
- // folderBrowserDialog
- //
- folderBrowserDialog.AddToRecent = false;
- //
- // ExportPreviewDialog
+ // ExportDialog
//
AcceptButton = button_Ok;
AutoScaleDimensions = new SizeF(11F, 24F);
AutoScaleMode = AutoScaleMode.Font;
CancelButton = button_Cancel;
- ClientSize = new Size(914, 482);
+ ClientSize = new Size(710, 698);
Controls.Add(panel1);
FormBorderStyle = FormBorderStyle.FixedDialog;
Icon = (Icon)resources.GetObject("$this.Icon");
MaximizeBox = false;
MinimizeBox = false;
- Name = "ExportPreviewDialog";
+ Name = "ExportDialog";
ShowInTaskbar = false;
StartPosition = FormStartPosition.CenterScreen;
- Text = "导出预览图";
+ Text = "导出参数";
panel1.ResumeLayout(false);
panel1.PerformLayout();
tableLayoutPanel1.ResumeLayout(false);
@@ -210,14 +147,9 @@
private Panel panel1;
private TableLayoutPanel tableLayoutPanel1;
- private Label label4;
- private Label label1;
- private TextBox textBox_OutputDir;
- private Button button_SelectOutputDir;
private TableLayoutPanel tableLayoutPanel2;
private Button button_Ok;
private Button button_Cancel;
- private FolderBrowserDialog folderBrowserDialog;
- private PropertyGrid propertyGrid;
+ private PropertyGrid propertyGrid_ExportArgs;
}
}
\ No newline at end of file
diff --git a/SpineViewer/Dialogs/ExportDialog.cs b/SpineViewer/Dialogs/ExportDialog.cs
new file mode 100644
index 0000000..194520e
--- /dev/null
+++ b/SpineViewer/Dialogs/ExportDialog.cs
@@ -0,0 +1,47 @@
+using SpineViewer.ExportHelper;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Drawing.Design;
+using System.Drawing.Imaging;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace SpineViewer.Dialogs
+{
+ public partial class ExportDialog: Form
+ {
+ ///
+ /// 要绑定的导出参数
+ ///
+ public required ExportArgs ExportArgs
+ {
+ get => propertyGrid_ExportArgs.SelectedObject as ExportArgs;
+ init => propertyGrid_ExportArgs.SelectedObject = value;
+ }
+
+ public ExportDialog()
+ {
+ InitializeComponent();
+ }
+
+ private void button_Ok_Click(object sender, EventArgs e)
+ {
+ if (ExportArgs.Validate() is string error)
+ {
+ MessageBox.Info(error, "参数错误");
+ return;
+ }
+ DialogResult = DialogResult.OK;
+ }
+
+ private void button_Cancel_Click(object sender, EventArgs e)
+ {
+ DialogResult = DialogResult.Cancel;
+ }
+ }
+}
diff --git a/SpineViewer/Dialogs/ExportPreviewDialog.resx b/SpineViewer/Dialogs/ExportDialog.resx
similarity index 99%
rename from SpineViewer/Dialogs/ExportPreviewDialog.resx
rename to SpineViewer/Dialogs/ExportDialog.resx
index d2f2a21..55a66f3 100644
--- a/SpineViewer/Dialogs/ExportPreviewDialog.resx
+++ b/SpineViewer/Dialogs/ExportDialog.resx
@@ -117,9 +117,6 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- 17, 17
-
diff --git a/SpineViewer/Dialogs/ExportPreviewDialog.cs b/SpineViewer/Dialogs/ExportPreviewDialog.cs
deleted file mode 100644
index 3519e53..0000000
--- a/SpineViewer/Dialogs/ExportPreviewDialog.cs
+++ /dev/null
@@ -1,169 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Data;
-using System.Drawing;
-using System.Drawing.Imaging;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows.Forms;
-
-namespace SpineViewer.Dialogs
-{
- public partial class ExportPreviewDialog: Form
- {
- ///
- /// 对话框结果
- ///
- public readonly ExportPreviewDialogResult Result = new();
-
- public ExportPreviewDialog()
- {
- InitializeComponent();
- propertyGrid.SelectedObject = Result;
- }
-
- private void button_SelectOutputDir_Click(object sender, EventArgs e)
- {
- folderBrowserDialog.InitialDirectory = textBox_OutputDir.Text;
- if (folderBrowserDialog.ShowDialog() != DialogResult.OK)
- return;
- textBox_OutputDir.Text = Path.GetFullPath(folderBrowserDialog.SelectedPath);
- }
-
- private void button_Ok_Click(object sender, EventArgs e)
- {
- var outputDir = textBox_OutputDir.Text;
- if (string.IsNullOrEmpty(outputDir))
- {
- outputDir = null;
- }
- else
- {
- if (File.Exists(outputDir))
- {
- MessageBox.Info("输出文件夹无效");
- return;
- }
-
- if (!Directory.Exists(outputDir))
- {
- if (MessageBox.Quest($"文件夹 {outputDir} 不存在,是否创建?") != DialogResult.OK)
- return;
-
- try
- {
- Directory.CreateDirectory(outputDir);
- }
- catch (Exception ex)
- {
- Program.Logger.Error(ex.ToString());
- MessageBox.Error(ex.ToString(), "文件夹创建失败");
- return;
- }
- }
- outputDir = Path.GetFullPath(outputDir);
- }
-
- if (outputDir is null && string.IsNullOrEmpty(Result.NameSuffix))
- {
- MessageBox.Info("输出文件夹和名称后缀不可同时为空,存在文件覆盖风险");
- return;
- }
-
- Result.OutputDir = outputDir;
- DialogResult = DialogResult.OK;
- }
-
- private void button_Cancel_Click(object sender, EventArgs e)
- {
- DialogResult = DialogResult.Cancel;
- }
- }
-
- public class ExportPreviewDialogResult
- {
- ///
- /// 输出路径
- ///
- [Browsable(false)]
- public string? OutputDir { get; set; } = null;
-
- ///
- /// 预览图格式
- ///
- [TypeConverter(typeof(ImageFormatConverter))]
- [Category("图像"), DisplayName("预览图格式")]
- public ImageFormat ImageFormat
- {
- get => imageFormat;
- set
- {
- if (value == ImageFormat.MemoryBmp) value = ImageFormat.Bmp;
- imageFormat = value;
- }
- }
- private ImageFormat imageFormat = ImageFormat.Png;
-
- ///
- /// 预览图分辨率
- ///
- [TypeConverter(typeof(SizeConverter))]
- [Category("图像"), DisplayName("分辨率")]
- public Size Resolution
- {
- get => resolution;
- set
- {
- if (value.Width <= 0) value.Width = 128;
- if (value.Height <= 0) value.Height = 128;
- resolution = value;
- }
- }
- private Size resolution = new(512, 512);
-
- ///
- /// 四周填充像素值
- ///
- [TypeConverter(typeof(PaddingConverter))]
- [Category("图像"), DisplayName("四周填充像素值")]
- public Padding Padding
- {
- get => padding;
- set
- {
- if (value.Left <= 0) value.Left = 10;
- if (value.Right <= 0) value.Right = 10;
- if (value.Top <= 0) value.Top = 10;
- if (value.Bottom <= 0) value.Bottom = 10;
- padding = value;
- }
- }
- private Padding padding = new(1);
-
- ///
- /// DPI
- ///
- [TypeConverter(typeof(SizeFConverter))]
- [Category("图像"), DisplayName("DPI")]
- public SizeF DPI
- {
- get => dpi;
- set
- {
- if (value.Width <= 0) value.Width = 144;
- if (value.Height <= 0) value.Height = 144;
- dpi = value;
- }
- }
- private SizeF dpi = new(144, 144);
-
- ///
- /// 名称后缀
- ///
- [Category("其他"), DisplayName("名称后缀")]
- public string NameSuffix { get; set; } = "(preview)";
- }
-
-}
diff --git a/SpineViewer/ExportHelper/ExportArgs.cs b/SpineViewer/ExportHelper/ExportArgs.cs
new file mode 100644
index 0000000..a6210a8
--- /dev/null
+++ b/SpineViewer/ExportHelper/ExportArgs.cs
@@ -0,0 +1,153 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing.Design;
+using System.Drawing.Imaging;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SpineViewer.ExportHelper
+{
+ ///
+ /// 导出参数基类
+ ///
+ public abstract class ExportArgs
+ {
+ ///
+ /// 输出文件夹
+ ///
+ [Editor(typeof(FolderNameEditor), typeof(UITypeEditor))]
+ [Category("导出"), DisplayName("输出文件夹"), Description("逐个导出时可以留空,将逐个导出到模型自身所在目录")]
+ public string? OutputDir { get; set; } = null;
+
+ ///
+ /// 逐个导出
+ ///
+ [Category("导出"), DisplayName("导出单个"), Description("是否将模型在同一个画面上导出单个文件,否则逐个导出模型")]
+ public bool ExportSingle { get; set; } = false;
+
+ ///
+ /// 画面分辨率
+ ///
+ [TypeConverter(typeof(SizeConverter))]
+ [Category("导出"), DisplayName("分辨率"), Description("画面的宽高像素大小")]
+ public required Size Resolution { get; init; }
+
+ ///
+ /// 渲染视窗
+ ///
+ [Browsable(false)]
+ public required SFML.Graphics.View View { get; init; }
+
+ ///
+ /// 是否仅渲染选中
+ ///
+ [Category("导出"), DisplayName("仅渲染选中"), Description("是否仅导出选中的模型")]
+ public required bool RenderSelectedOnly { get; init; }
+
+ ///
+ /// 检查参数是否合法并规范化参数值, 否则返回用户错误原因
+ ///
+ ///
+ public virtual string? Validate()
+ {
+ if (!string.IsNullOrEmpty(OutputDir) && File.Exists(OutputDir))
+ return "输出文件夹无效";
+ if (!string.IsNullOrEmpty(OutputDir) && !Directory.Exists(OutputDir))
+ return $"文件夹 {OutputDir} 不存在";
+ if (ExportSingle && string.IsNullOrEmpty(OutputDir))
+ return "导出单个时必须提供输出文件夹";
+
+ OutputDir = string.IsNullOrEmpty(OutputDir) ? null : Path.GetFullPath(OutputDir);
+ return null;
+ }
+ }
+
+ ///
+ /// 画面帧导出参数
+ ///
+ public class ExportFrameArgs : ExportArgs
+ {
+ ///
+ /// 名称后缀
+ ///
+ [Category("画面帧"), DisplayName("名称后缀"), Description("逐个导出时必须提供该值,否则存在文件覆盖风险")]
+ public string NameSuffix { get; set; } = "(preview)";
+
+ ///
+ /// 画面帧格式
+ ///
+ [TypeConverter(typeof(ImageFormatConverter))]
+ [Category("画面帧"), DisplayName("图像格式")]
+ public ImageFormat ImageFormat
+ {
+ get => imageFormat;
+ set
+ {
+ if (value == ImageFormat.MemoryBmp) value = ImageFormat.Bmp;
+ imageFormat = value;
+ }
+ }
+ private ImageFormat imageFormat = ImageFormat.Png;
+
+ ///
+ /// 文件名后缀
+ ///
+ [Category("画面帧"), DisplayName("文件名后缀"), Description("与图像格式匹配的文件名后缀")]
+ public string FileSuffix { get => imageFormat.GetSuffix(); }
+
+ ///
+ /// 四周填充像素值
+ ///
+ [TypeConverter(typeof(PaddingConverter))]
+ [Category("画面帧"), DisplayName("四周填充像素值"), Description("在图内四周留出来的透明像素区域, 画面内容的可用范围是分辨率裁去填充区域")]
+ public Padding Padding
+ {
+ get => padding;
+ set
+ {
+ if (value.Left < 0) value.Left = 0;
+ if (value.Right < 0) value.Right = 0;
+ if (value.Top < 0) value.Top = 0;
+ if (value.Bottom < 0) value.Bottom = 0;
+ padding = value;
+ }
+ }
+ private Padding padding = new(1);
+
+ ///
+ /// DPI
+ ///
+ [TypeConverter(typeof(SizeFConverter))]
+ [Category("画面帧"), DisplayName("DPI"), Description("导出图像的每英寸像素数,用于调整图像的物理尺寸")]
+ public SizeF DPI
+ {
+ get => dpi;
+ set
+ {
+ if (value.Width <= 0) value.Width = 144;
+ if (value.Height <= 0) value.Height = 144;
+ dpi = value;
+ }
+ }
+ private SizeF dpi = new(144, 144);
+
+ public override string? Validate()
+ {
+ if (base.Validate() is string error)
+ return error;
+ if (string.IsNullOrEmpty(OutputDir) && string.IsNullOrEmpty(NameSuffix))
+ return "输出文件夹和名称后缀不可同时为空,存在文件覆盖风险";
+ return null;
+ }
+ }
+
+ ///
+ /// 视频导出参数基类
+ ///
+ public abstract class ExportVideoArgs:ExportArgs
+ {
+
+ }
+}
diff --git a/SpineViewer/ExportHelper/ExportHelper.cs b/SpineViewer/ExportHelper/ExportHelper.cs
index 10beedc..c588a0f 100644
--- a/SpineViewer/ExportHelper/ExportHelper.cs
+++ b/SpineViewer/ExportHelper/ExportHelper.cs
@@ -38,5 +38,47 @@ namespace SpineViewer.ExportHelper
else if (imageFormat == ImageFormat.Exif) return ".jpg";
else return $".{imageFormat.ToString().ToLower()}";
}
+
+ ///
+ /// 获取某个包围盒下合适的视图
+ ///
+ public static SFML.Graphics.View GetView(this RectangleF bounds, Size resolution, Padding padding)
+ => GetView(bounds, (uint)resolution.Width, (uint)resolution.Height, (uint)padding.Left, (uint)padding.Right, (uint)padding.Top, (uint)padding.Bottom);
+
+ ///
+ /// 获取某个包围盒下合适的视图
+ ///
+ public static SFML.Graphics.View GetView(this RectangleF bounds, uint width, uint height, Padding padding)
+ => GetView(bounds, width, height, (uint)padding.Left, (uint)padding.Right, (uint)padding.Top, (uint)padding.Bottom);
+
+ ///
+ /// 获取某个包围盒下合适的视图
+ ///
+ public static SFML.Graphics.View GetView(this RectangleF bounds, Size resolution, uint paddingL = 1, uint paddingR = 1, uint paddingT = 1, uint paddingB = 1)
+ => GetView(bounds, (uint)resolution.Width, (uint)resolution.Height, paddingL, paddingR, paddingT, paddingB);
+
+ ///
+ /// 获取某个包围盒下合适的视图
+ ///
+ public static SFML.Graphics.View GetView(this RectangleF bounds, uint width, uint height, uint paddingL = 1, uint paddingR = 1, uint paddingT = 1, uint paddingB = 1)
+ {
+ float sizeX = bounds.Width;
+ float sizeY = bounds.Height;
+ float innerW = width - paddingL - paddingR;
+ float innerH = height - paddingT - paddingB;
+
+ float scale = 1;
+ if ((sizeY / sizeX) < (innerH / innerW))
+ scale = sizeX / innerW; // 相同的 X, 视窗 Y 更大
+ else
+ scale = sizeY / innerH; // 相同的 Y, 视窗 X 更大
+
+ var x = bounds.X + bounds.Width / 2 + ((float)paddingL - (float)paddingR) * scale;
+ var y = bounds.Y + bounds.Height / 2 + ((float)paddingT - (float)paddingB) * scale;
+ var viewX = width * scale;
+ var viewY = height * scale;
+
+ return new(new(x, y), new(viewX, -viewY));
+ }
}
}
diff --git a/SpineViewer/MainForm.Designer.cs b/SpineViewer/MainForm.Designer.cs
index 476d91f..b09b994 100644
--- a/SpineViewer/MainForm.Designer.cs
+++ b/SpineViewer/MainForm.Designer.cs
@@ -36,7 +36,13 @@
toolStripMenuItem_BatchOpen = new ToolStripMenuItem();
toolStripSeparator1 = new ToolStripSeparator();
toolStripMenuItem_Export = new ToolStripMenuItem();
- toolStripMenuItem_ExportPreview = new ToolStripMenuItem();
+ toolStripMenuItem_ExportFrame = new ToolStripMenuItem();
+ toolStripMenuItem_ExportFrames = new ToolStripMenuItem();
+ toolStripMenuItem_ExportGif = new ToolStripMenuItem();
+ toolStripMenuItem_ExportMkv = new ToolStripMenuItem();
+ toolStripMenuItem_ExportMp4 = new ToolStripMenuItem();
+ toolStripMenuItem_ExportMov = new ToolStripMenuItem();
+ toolStripMenuItem_ExportWebm = new ToolStripMenuItem();
toolStripSeparator2 = new ToolStripSeparator();
toolStripMenuItem_Exit = new ToolStripMenuItem();
toolStripMenuItem_Tool = new ToolStripMenuItem();
@@ -99,7 +105,7 @@
//
// toolStripMenuItem_File
//
- toolStripMenuItem_File.DropDownItems.AddRange(new ToolStripItem[] { toolStripMenuItem_Open, toolStripMenuItem_BatchOpen, toolStripSeparator1, toolStripMenuItem_Export, toolStripMenuItem_ExportPreview, toolStripSeparator2, toolStripMenuItem_Exit });
+ toolStripMenuItem_File.DropDownItems.AddRange(new ToolStripItem[] { toolStripMenuItem_Open, toolStripMenuItem_BatchOpen, toolStripSeparator1, toolStripMenuItem_Export, toolStripSeparator2, toolStripMenuItem_Exit });
toolStripMenuItem_File.Name = "toolStripMenuItem_File";
toolStripMenuItem_File.Size = new Size(84, 28);
toolStripMenuItem_File.Text = "文件(&F)";
@@ -108,47 +114,83 @@
//
toolStripMenuItem_Open.Name = "toolStripMenuItem_Open";
toolStripMenuItem_Open.ShortcutKeys = Keys.Control | Keys.O;
- toolStripMenuItem_Open.Size = new Size(254, 34);
+ toolStripMenuItem_Open.Size = new Size(270, 34);
toolStripMenuItem_Open.Text = "打开(&O)...";
toolStripMenuItem_Open.Click += toolStripMenuItem_Open_Click;
//
// toolStripMenuItem_BatchOpen
//
toolStripMenuItem_BatchOpen.Name = "toolStripMenuItem_BatchOpen";
- toolStripMenuItem_BatchOpen.Size = new Size(254, 34);
+ toolStripMenuItem_BatchOpen.Size = new Size(270, 34);
toolStripMenuItem_BatchOpen.Text = "批量打开(&B)...";
toolStripMenuItem_BatchOpen.Click += toolStripMenuItem_BatchOpen_Click;
//
// toolStripSeparator1
//
toolStripSeparator1.Name = "toolStripSeparator1";
- toolStripSeparator1.Size = new Size(251, 6);
+ toolStripSeparator1.Size = new Size(267, 6);
//
// toolStripMenuItem_Export
//
+ toolStripMenuItem_Export.DropDownItems.AddRange(new ToolStripItem[] { toolStripMenuItem_ExportFrame, toolStripMenuItem_ExportFrames, toolStripMenuItem_ExportGif, toolStripMenuItem_ExportMkv, toolStripMenuItem_ExportMp4, toolStripMenuItem_ExportMov, toolStripMenuItem_ExportWebm });
toolStripMenuItem_Export.Name = "toolStripMenuItem_Export";
- toolStripMenuItem_Export.ShortcutKeys = Keys.Control | Keys.S;
- toolStripMenuItem_Export.Size = new Size(254, 34);
- toolStripMenuItem_Export.Text = "导出(&E)...";
- toolStripMenuItem_Export.Click += toolStripMenuItem_Export_Click;
+ toolStripMenuItem_Export.Size = new Size(270, 34);
+ toolStripMenuItem_Export.Text = "导出(&E)";
//
- // toolStripMenuItem_ExportPreview
+ // toolStripMenuItem_ExportFrame
//
- toolStripMenuItem_ExportPreview.Name = "toolStripMenuItem_ExportPreview";
- toolStripMenuItem_ExportPreview.Size = new Size(254, 34);
- toolStripMenuItem_ExportPreview.Text = "导出预览图(&P)...";
- toolStripMenuItem_ExportPreview.Click += toolStripMenuItem_ExportPreview_Click;
+ toolStripMenuItem_ExportFrame.Name = "toolStripMenuItem_ExportFrame";
+ toolStripMenuItem_ExportFrame.Size = new Size(270, 34);
+ toolStripMenuItem_ExportFrame.Text = "单帧画面...";
+ toolStripMenuItem_ExportFrame.Click += toolStripMenuItem_ExportFrame_Click;
+ //
+ // toolStripMenuItem_ExportFrames
+ //
+ toolStripMenuItem_ExportFrames.Name = "toolStripMenuItem_ExportFrames";
+ toolStripMenuItem_ExportFrames.Size = new Size(270, 34);
+ toolStripMenuItem_ExportFrames.Text = "帧序列...";
+ toolStripMenuItem_ExportFrames.Click += toolStripMenuItem_ExportPng_Click;
+ //
+ // toolStripMenuItem_ExportGif
+ //
+ toolStripMenuItem_ExportGif.Name = "toolStripMenuItem_ExportGif";
+ toolStripMenuItem_ExportGif.Size = new Size(270, 34);
+ toolStripMenuItem_ExportGif.Text = "GIF...";
+ //
+ // toolStripMenuItem_ExportMkv
+ //
+ toolStripMenuItem_ExportMkv.Name = "toolStripMenuItem_ExportMkv";
+ toolStripMenuItem_ExportMkv.Size = new Size(270, 34);
+ toolStripMenuItem_ExportMkv.Text = "MKV";
+ //
+ // toolStripMenuItem_ExportMp4
+ //
+ toolStripMenuItem_ExportMp4.Name = "toolStripMenuItem_ExportMp4";
+ toolStripMenuItem_ExportMp4.Size = new Size(270, 34);
+ toolStripMenuItem_ExportMp4.Text = "MP4...";
+ //
+ // toolStripMenuItem_ExportMov
+ //
+ toolStripMenuItem_ExportMov.Name = "toolStripMenuItem_ExportMov";
+ toolStripMenuItem_ExportMov.Size = new Size(270, 34);
+ toolStripMenuItem_ExportMov.Text = "MOV...";
+ //
+ // toolStripMenuItem_ExportWebm
+ //
+ toolStripMenuItem_ExportWebm.Name = "toolStripMenuItem_ExportWebm";
+ toolStripMenuItem_ExportWebm.Size = new Size(270, 34);
+ toolStripMenuItem_ExportWebm.Text = "WebM...";
//
// toolStripSeparator2
//
toolStripSeparator2.Name = "toolStripSeparator2";
- toolStripSeparator2.Size = new Size(251, 6);
+ toolStripSeparator2.Size = new Size(267, 6);
//
// toolStripMenuItem_Exit
//
toolStripMenuItem_Exit.Name = "toolStripMenuItem_Exit";
toolStripMenuItem_Exit.ShortcutKeys = Keys.Alt | Keys.F4;
- toolStripMenuItem_Exit.Size = new Size(254, 34);
+ toolStripMenuItem_Exit.Size = new Size(270, 34);
toolStripMenuItem_Exit.Text = "退出(&X)";
toolStripMenuItem_Exit.Click += toolStripMenuItem_Exit_Click;
//
@@ -162,7 +204,7 @@
// toolStripMenuItem_ConvertFileFormat
//
toolStripMenuItem_ConvertFileFormat.Name = "toolStripMenuItem_ConvertFileFormat";
- toolStripMenuItem_ConvertFileFormat.Size = new Size(270, 34);
+ toolStripMenuItem_ConvertFileFormat.Size = new Size(254, 34);
toolStripMenuItem_ConvertFileFormat.Text = "转换文件格式(&C)...";
toolStripMenuItem_ConvertFileFormat.Click += toolStripMenuItem_ConvertFileFormat_Click;
//
@@ -464,7 +506,6 @@
private ToolStripMenuItem toolStripMenuItem_Open;
private ToolStripMenuItem toolStripMenuItem_Exit;
private ToolStripSeparator toolStripSeparator1;
- private ToolStripMenuItem toolStripMenuItem_Export;
private ToolStripSeparator toolStripSeparator2;
private RichTextBox rtbLog;
private SplitContainer splitContainer_MainForm;
@@ -490,6 +531,13 @@
private ToolStripMenuItem toolStripMenuItem_ManageResource;
private ToolStripMenuItem toolStripMenuItem_Tool;
private ToolStripMenuItem toolStripMenuItem_ConvertFileFormat;
- private ToolStripMenuItem toolStripMenuItem_ExportPreview;
+ private ToolStripMenuItem toolStripMenuItem_Export;
+ private ToolStripMenuItem toolStripMenuItem_ExportFrame;
+ private ToolStripMenuItem toolStripMenuItem_ExportFrames;
+ private ToolStripMenuItem toolStripMenuItem_ExportGif;
+ private ToolStripMenuItem toolStripMenuItem_ExportMp4;
+ private ToolStripMenuItem toolStripMenuItem_ExportMov;
+ private ToolStripMenuItem toolStripMenuItem_ExportMkv;
+ private ToolStripMenuItem toolStripMenuItem_ExportWebm;
}
}
diff --git a/SpineViewer/MainForm.cs b/SpineViewer/MainForm.cs
index f75f145..b6f1268 100644
--- a/SpineViewer/MainForm.cs
+++ b/SpineViewer/MainForm.cs
@@ -8,6 +8,7 @@ using System.Diagnostics;
using System.Text.Json;
using System.Text.Json.Nodes;
using FFMpegCore.Enums;
+using SpineViewer.ExportHelper;
namespace SpineViewer
{
@@ -49,12 +50,12 @@ namespace SpineViewer
private void MainForm_Load(object sender, EventArgs e)
{
- spinePreviewer.StartPreview();
+ spinePreviewer.StartRender();
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
- spinePreviewer.StopPreview();
+ spinePreviewer.StopRender();
}
private void toolStripMenuItem_Open_Click(object sender, EventArgs e)
@@ -67,7 +68,42 @@ namespace SpineViewer
spineListView.BatchAdd();
}
- private void toolStripMenuItem_Export_Click(object sender, EventArgs e)
+ private void toolStripMenuItem_ExportFrame_Click(object sender, EventArgs e)
+ {
+ lock (spineListView.Spines)
+ {
+ if (spineListView.Spines.Count <= 0)
+ {
+ MessageBox.Info("请至少打开一个骨骼文件");
+ return;
+ }
+ }
+
+ if (spinePreviewer.IsUpdating)
+ {
+ if (MessageBox.Quest("画面仍在更新,是否手动暂停画面后再导出画面帧?") == DialogResult.OK)
+ return;
+ }
+
+ var exportDialog = new Dialogs.ExportDialog()
+ {
+ ExportArgs = new ExportFrameArgs()
+ {
+ Resolution = spinePreviewer.Resolution,
+ View = spinePreviewer.GetView(),
+ RenderSelectedOnly = spinePreviewer.RenderSelectedOnly,
+ }
+ };
+ if (exportDialog.ShowDialog() != DialogResult.OK)
+ return;
+
+ var progressDialog = new Dialogs.ProgressDialog();
+ progressDialog.DoWork += ExportFrame_Work;
+ progressDialog.RunWorkerAsync(exportDialog.ExportArgs);
+ progressDialog.ShowDialog();
+ }
+
+ private void toolStripMenuItem_ExportPng_Click(object sender, EventArgs e)
{
// TODO: 改成统一导出调用
lock (spineListView.Spines)
@@ -89,27 +125,6 @@ namespace SpineViewer
progressDialog.ShowDialog();
}
- private void toolStripMenuItem_ExportPreview_Click(object sender, EventArgs e)
- {
- lock (spineListView.Spines)
- {
- if (spineListView.Spines.Count <= 0)
- {
- MessageBox.Info("请至少打开一个骨骼文件");
- return;
- }
- }
-
- var saveDialog = new Dialogs.ExportPreviewDialog();
- if (saveDialog.ShowDialog() != DialogResult.OK)
- return;
-
- var progressDialog = new Dialogs.ProgressDialog();
- progressDialog.DoWork += ExportPreview_Work;
- progressDialog.RunWorkerAsync(saveDialog.Result);
- progressDialog.ShowDialog();
- }
-
private void toolStripMenuItem_Exit_Click(object sender, EventArgs e)
{
Close();
@@ -223,16 +238,15 @@ namespace SpineViewer
var fps = arguments.Fps;
var timestamp = DateTime.Now.ToString("yyMMddHHmmss");
- var frameArgs = spinePreviewer.GetFrameArgs();
var renderSelectedOnly = spinePreviewer.RenderSelectedOnly;
- var resolution = frameArgs.Resolution;
+ var resolution = spinePreviewer.Resolution;
var tex = new SFML.Graphics.RenderTexture((uint)resolution.Width, (uint)resolution.Height);
- tex.SetView(frameArgs.View);
+ tex.SetView(spinePreviewer.GetView());
var delta = 1f / fps;
var frameCount = 1 + (int)(duration / delta); // 零帧开始导出
- spinePreviewer.StopPreview();
+ spinePreviewer.StopRender();
lock (spineListView.Spines)
{
@@ -279,31 +293,27 @@ namespace SpineViewer
Program.Logger.Info("Exporting done: {}/{}", success, frameCount);
}
- spinePreviewer.StartPreview();
+ spinePreviewer.StartRender();
}
- private void ExportPreview_Work(object? sender, DoWorkEventArgs e)
+ private void ExportFrame_Work(object? sender, DoWorkEventArgs e)
{
- var worker = sender as BackgroundWorker;
- var arguments = e.Argument as Dialogs.ExportPreviewDialogResult;
+ var worker = (BackgroundWorker)sender;
+ var args = (ExportFrameArgs)e.Argument;
- var outputDir = arguments.OutputDir;
- var imageFormat = arguments.ImageFormat;
- var resolution = arguments.Resolution;
- var padding = arguments.Padding;
- var dpi = arguments.DPI;
- var nameSuffix = arguments.NameSuffix;
- var renderSelectedOnly = spinePreviewer.RenderSelectedOnly;
-
- var tex = new SFML.Graphics.RenderTexture((uint)resolution.Width, (uint)resolution.Height);
+ using var tex = new SFML.Graphics.RenderTexture((uint)args.Resolution.Width, (uint)args.Resolution.Height);
+ tex.Clear(SFML.Graphics.Color.Transparent);
+ tex.SetView(args.View);
int success = 0;
int error = 0;
- spinePreviewer.StopPreview();
+ spinePreviewer.StopRender();
lock (spineListView.Spines)
{
- var spines = spineListView.Spines;
- int totalCount = spines.Count;
+ // 根据是否仅渲染选中得到要渲染的模型数组
+ var spines = spineListView.Spines.Where(sp => !args.RenderSelectedOnly || sp.IsSelected).Reverse().ToArray();
+
+ int totalCount = spines.Length;
worker.ReportProgress(0, $"已处理 0/{totalCount}");
for (int i = 0; i < totalCount; i++)
{
@@ -314,49 +324,73 @@ namespace SpineViewer
}
var spine = spines[i];
- if (renderSelectedOnly && !spine.IsSelected)
- continue;
-
- var filename = $"{spine.Name}{nameSuffix}{imageFormat.GetSuffix()}";
- var savePath = outputDir is null ? Path.Combine(spine.AssetsDir, filename) : Path.Combine(outputDir, filename);
-
- var tmp = spine.CurrentAnimation;
- spine.CurrentAnimation = Spine.Spine.EMPTY_ANIMATION;
- tex.SetView(spine.GetInitView(resolution, padding));
- tex.Clear(SFML.Graphics.Color.Transparent);
tex.Draw(spine);
+
+ if (args.ExportSingle)
+ {
+ // 导出单个则直接算成功
+ success++;
+ }
+ else
+ {
+ // 逐个导出则立即渲染, 并且保存完之后需要清除画面
+ tex.Display();
+
+ var filename = $"{spine.Name}{args.NameSuffix}{args.FileSuffix}";
+ var savePath = args.OutputDir is null ? Path.Combine(spine.AssetsDir, filename) : Path.Combine(args.OutputDir, filename);
+ try
+ {
+ using (var img = new Bitmap(tex.Texture.CopyToBitmap()))
+ {
+ img.SetResolution(args.DPI.Width, args.DPI.Height);
+ img.Save(savePath, args.ImageFormat);
+ }
+ success++;
+ }
+ catch (Exception ex)
+ {
+ Program.Logger.Error(ex.ToString());
+ Program.Logger.Error("Failed to save frame {}", spine.SkelPath);
+ error++;
+ }
+
+ tex.Clear(SFML.Graphics.Color.Transparent);
+ }
+
+ worker.ReportProgress((int)((i + 1) * 100.0) / totalCount, $"已处理 {i + 1}/{totalCount}");
+ }
+
+ // 导出单个
+ if (args.ExportSingle)
+ {
tex.Display();
- spine.CurrentAnimation = tmp;
+
+ var filename = $"{DateTime.Now:yyMMddHHmmss}{args.NameSuffix}{args.FileSuffix}";
+ var savePath = Path.Combine(args.OutputDir, filename);
try
{
using (var img = new Bitmap(tex.Texture.CopyToBitmap()))
{
- img.SetResolution(dpi.Width, dpi.Height);
- img.Save(savePath, imageFormat);
+ img.SetResolution(args.DPI.Width, args.DPI.Height);
+ img.Save(savePath, args.ImageFormat);
}
- success++;
}
catch (Exception ex)
{
Program.Logger.Error(ex.ToString());
- Program.Logger.Error("Failed to save preview {}", spine.SkelPath);
- error++;
+ Program.Logger.Error("Failed to save single frame");
}
-
- worker.ReportProgress((int)((i + 1) * 100.0) / totalCount, $"已处理 {i + 1}/{totalCount}");
+ }
+ else
+ {
+ if (error > 0)
+ Program.Logger.Warn("Frames save {} successfully, {} failed", success, error);
+ else
+ Program.Logger.Info("{} frames saved successfully", success);
}
}
- spinePreviewer.StartPreview();
-
- if (error > 0)
- {
- Program.Logger.Warn("Preview save {} successfully, {} failed", success, error);
- }
- else
- {
- Program.Logger.Info("{} preview saved successfully", success);
- }
+ spinePreviewer.StartRender();
Program.LogCurrentMemoryUsage();
}
diff --git a/SpineViewer/Spine/Spine.cs b/SpineViewer/Spine/Spine.cs
index 436ac60..fd881f5 100644
--- a/SpineViewer/Spine/Spine.cs
+++ b/SpineViewer/Spine/Spine.cs
@@ -14,6 +14,7 @@ using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Text.Json.Nodes;
using System.Collections.Immutable;
+using SpineViewer.ExportHelper;
namespace SpineViewer.Spine
{
@@ -346,6 +347,26 @@ namespace SpineViewer.Spine
[Browsable(false)]
public abstract RectangleF Bounds { get; }
+ ///
+ /// 初始状态下的骨骼包围盒
+ ///
+ [Browsable(false)]
+ public RectangleF InitBounds
+ {
+ get
+ {
+ if (initBounds is null)
+ {
+ var tmp = CurrentAnimation;
+ CurrentAnimation = EMPTY_ANIMATION;
+ initBounds = Bounds;
+ CurrentAnimation = tmp;
+ }
+ return (RectangleF)initBounds;
+ }
+ }
+ private RectangleF? initBounds = null;
+
///
/// 骨骼预览图
///
@@ -362,7 +383,7 @@ namespace SpineViewer.Spine
// 除此之外, 似乎还和 tex 的 Dispose 有关
// 如果不对 tex 进行 Dispose, 那么不管是否 Draw 都正常不会死锁
var tex = new SFML.Graphics.RenderTexture(PREVIEW_WIDTH, PREVIEW_HEIGHT);
- tex.SetView(GetInitView(PREVIEW_WIDTH, PREVIEW_HEIGHT));
+ tex.SetView(InitBounds.GetView(PREVIEW_WIDTH, PREVIEW_HEIGHT));
tex.Clear(SFML.Graphics.Color.Transparent);
var tmp = CurrentAnimation;
CurrentAnimation = EMPTY_ANIMATION;
@@ -391,53 +412,6 @@ namespace SpineViewer.Spine
/// 时间间隔
public abstract void Update(float delta);
- ///
- /// 获取初始状态下合适的 View, 参数单位为像素
- ///
- public SFML.Graphics.View GetInitView(Size resolution, Padding padding) =>
- GetInitView((uint)resolution.Width, (uint)resolution.Height, (uint)padding.Left, (uint)padding.Right, (uint)padding.Top, (uint)padding.Bottom);
-
- ///
- /// 获取初始状态下合适的 View, 参数单位为像素
- ///
- public SFML.Graphics.View GetInitView(uint width, uint height, Padding padding) =>
- GetInitView(width, height, (uint)padding.Left, (uint)padding.Right, (uint)padding.Top, (uint)padding.Bottom);
-
- ///
- /// 获取初始状态下合适的 View, 参数单位为像素
- ///
- public SFML.Graphics.View GetInitView(Size resolution, uint paddingL = 1, uint paddingR = 1, uint paddingT = 1, uint paddingB = 1) =>
- GetInitView((uint)resolution.Width, (uint)resolution.Height, paddingL, paddingR, paddingT, paddingB);
-
- ///
- /// 获取初始状态下合适的 View, 参数单位为像素
- ///
- public SFML.Graphics.View GetInitView(uint width, uint height, uint paddingL = 1, uint paddingR = 1, uint paddingT = 1, uint paddingB = 1)
- {
- var tmp = CurrentAnimation;
- CurrentAnimation = EMPTY_ANIMATION;
- var bounds = Bounds;
- CurrentAnimation = tmp;
-
- float sizeX = bounds.Width;
- float sizeY = bounds.Height;
- float innerW = width - paddingL - paddingR;
- float innerH = height - paddingT - paddingB;
-
- float scale = 1;
- if ((sizeY / sizeX) < (innerH / innerW))
- scale = sizeX / innerW; // 相同的 X, 视窗 Y 更大
- else
- scale = sizeY / innerH; // 相同的 Y, 视窗 X 更大
-
- var x = bounds.X + bounds.Width / 2 + ((float)paddingL - (float)paddingR) * scale;
- var y = bounds.Y + bounds.Height / 2 + ((float)paddingT - (float)paddingB) * scale;
- var viewX = width * scale;
- var viewY = height * scale;
-
- return new(new(x, y), new(viewX, -viewY));
- }
-
///
/// 是否被选中
///