Compare commits

..

13 Commits

Author SHA1 Message Date
ww-rm
4e293daf62 更新至v0.10.9 2025-03-24 15:18:09 +08:00
ww-rm
f9d7fdc516 update readme 2025-03-24 15:17:42 +08:00
ww-rm
6a04f3955c 完善预览图导出参数 2025-03-24 15:15:59 +08:00
ww-rm
dce3b1780c update readme 2025-03-24 14:44:44 +08:00
ww-rm
f47f3e9db6 更新至v0.10.8 2025-03-24 14:42:21 +08:00
ww-rm
4ac74acaf7 update changelog 2025-03-24 14:42:11 +08:00
ww-rm
cf7588c288 update readme 2025-03-24 14:41:25 +08:00
ww-rm
ec7bdf4000 预览图增加仅导出选中 2025-03-24 14:33:09 +08:00
ww-rm
51cd97f782 调整布局 2025-03-24 13:48:05 +08:00
ww-rm
a16f2f096d 完善预览图导出 2025-03-24 13:47:56 +08:00
ww-rm
4e92f14551 完善文件转换功能 2025-03-24 01:58:53 +08:00
ww-rm
8f6cc9ff44 增加任意格式读取 2025-03-24 01:57:49 +08:00
ww-rm
f885df5c67 增加文件选择控件 2025-03-24 01:57:13 +08:00
17 changed files with 972 additions and 487 deletions

View File

@@ -1,5 +1,14 @@
# CHANGELOG # CHANGELOG
## v0.10.9
- 预览图导出增加名称后缀参数
## v0.10.8
- 完善预览图导出
- 优化骨骼文件选择
## v0.10.7 ## v0.10.7
- 增加仅导出选中 - 增加仅导出选中

View File

@@ -4,7 +4,7 @@
[中文](README.md) | [English](README.en.md) [中文](README.md) | [English](README.en.md)
A simple and user-friendly Spine file viewer and exporter. A simple and user-friendly tool for viewing and exporting Spine files.
![previewer](img/preview.webp) ![previewer](img/preview.webp)
@@ -12,59 +12,81 @@ A simple and user-friendly Spine file viewer and exporter.
## Installation ## Installation
Download the zip package from the [Releases](https://github.com/ww-rm/SpineViewer/releases) page. Go to the [Release](https://github.com/ww-rm/SpineViewer/releases) page to download the compressed package.
The application requires the [.NET Desktop Runtime 8.0.x](https://dotnet.microsoft.com/en-us/download/dotnet/8.0). The software requires the dependency framework [.NET Desktop Runtime 8.0.x](https://dotnet.microsoft.com/zh-cn/download/dotnet/8.0).
Alternatively, you can download the zip package with the `SelfContained` suffix, which can run independently. You can also download the package with the `SelfContained` suffix, which can run independently.
## Features ## Version Support
- Supports viewing Spine files of different versions: | Version | View & Export | Format Conversion | Version Conversion |
- [x] `v2.1.x` | :-------: | :--------------------: | :--------------------: | :----------------: |
- [x] `v3.6.x` | `2.1.x` | :white_check_mark: | | |
- [x] `v3.7.x` | `3.1.x` | | | |
- [x] `v3.8.x` | `3.4.x` | | | |
- [x] `v4.0.x` | `3.5.x` | | | |
- [x] `v4.1.x` | `3.6.x` | :white_check_mark: | | |
- [x] `v4.2.x` | `3.7.x` | :white_check_mark: | | |
- [ ] `v4.3.x` | `3.8.x` | :white_check_mark: | :white_check_mark: | |
- Supports animation preview for multi-skeleton files | `4.1.x` | :white_check_mark: | | |
- Allows independent parameter settings for each skeleton | `4.2.x` | :white_check_mark: | | |
- Supports exporting animation as PNG frame sequences | `4.3.x` | | | |
- Provides export settings such as zoom and rotation
- More features coming soon...
## Usage ## Usage
### Importing Skeletons ### Importing Skeletons
Use the **File** menu to select **Open** or **Batch Open** to import skeleton files. There are 3 ways to import skeleton files:
### Adjusting Skeletons - **Drag & Drop/Paste:**
Drag and drop or paste the skeleton file/directory into the model list.
This method automatically searches through the provided files and subdirectories. Although convenient, it relies on the file structure and has its limitations.
- Only standard files with `*.json`, `*.skel`, or `.atlas` extensions are automatically detected.
- The skeleton file and atlas file must have the same name.
- The version string in the skeleton file must not be modified.
Select one or more items in the **Model List** to display adjustable parameters in the **Model Parameters** panel. - **Batch Open from the File Menu:**
This method offers more file flexibility. You can drag and drop or paste files into the file selection dialog, and additional options are available.
- The filename restrictions are similar to the above, but you can use the panels file selection button to choose skeleton files with non-standard extensions.
- You can set a fixed load version to handle cases where the version number has been modified.
Right-clicking in the **Model List** allows you to add, delete, or adjust list items. You can also drag items with the left mouse button to rearrange them. - **Open a Single Model:**
This method offers the highest degree of freedom, allowing you to select any skeleton file and atlas file without filename restrictions. You can also set the load version.
### Adjusting the View ### Adjusting Preview Content
Mouse operations supported in the **Preview** window: The model list supports right-click menus and various shortcut keys, and you can select multiple models to adjust their parameters in bulk.
- Left-click to drag the skeleton In addition to the parameter panel, the preview area supports several mouse actions:
- Right-click to drag the view
- Scroll wheel to zoom in/out
Additionally, you can adjust export and preview parameters through the **View Parameters** panel. - **Left-click:** Select and drag models. Holding down the `Ctrl` key enables multi-selection, which syncs with the model list.
- **Right-click:** Drag the overall canvas.
- **Scroll wheel:** Zoom the view.
- **Selective Rendering:** The preview area supports a mode to render only the selected models. In this mode, only the selected models are displayed, and selection changes must be made through the model list.
In the **Functions** menu, you can reset and synchronize the animation time for all skeletons. In the function menu, you can reset and synchronize the animation time for all skeletons.
### Exporting Animations ### Exporting Preview Content
Select **Export** from the **File** menu to export all loaded skeleton animations as PNG frame sequences, based on the current preview settings. Both preview images and videos can be exported.
You can view the full duration of each animation in the **Model Parameters** of each skeleton. - **Preview Image:**
The exported preview image shows the model in its default state, with one image per model.
- **Video (TODO: Currently only supports frame sequence export):**
The complete animation duration for each skeleton can be viewed in the model parameters.
When the preview area is set to render only the selected models, the exported content will include only the models that are displayed.
### Format & Version Conversion
You can use the tools menu to convert skeleton files. This feature supports conversion between binary and text formats, as well as between different versions.
Currently under development, it only supports converting `3.8.x` binary format to text format.
--- ---
*If you find this project helpful, please give it a :star: and share it with others! :)* *If you like this project, please give it a :star: and share it with more people! :)*

View File

@@ -18,62 +18,72 @@
也可以下载带有 `SelfContained` 后缀的压缩包, 可以独立运行. 也可以下载带有 `SelfContained` 后缀的压缩包, 可以独立运行.
## 功能支持 ## 版本支持
| 版本 | 查看&导出 | 格式转换 | | 版本 | 查看&导出 | 格式转换 | 版本转换 |
| :---: | :---: | :---: | | :---: | :---: | :---: | :---: |
| `2.1.x` | :white_check_mark: | | | `2.1.x` | :white_check_mark: | | |
| `3.1.x` | | | | `3.1.x` | | | |
| `3.4.x` | | | | `3.4.x` | | | |
| `3.5.x` | | | | `3.5.x` | | | |
| `3.6.x` | :white_check_mark: | | | `3.6.x` | :white_check_mark: | | |
| `3.7.x` | :white_check_mark: | | | `3.7.x` | :white_check_mark: | | |
| `3.8.x` | :white_check_mark: | :white_check_mark: | | `3.8.x` | :white_check_mark: | :white_check_mark: | |
| `4.1.x` | :white_check_mark: | | | `4.1.x` | :white_check_mark: | | |
| `4.2.x` | :white_check_mark: | | | `4.2.x` | :white_check_mark: | | |
| `4.3.x` | | | | `4.3.x` | | | |
- 支持文件拖放/复制到剪贴板打开
- 支持自动检测版本
- 支持列表缩略图预览
- 支持多骨骼文件动画预览
- 支持每个骨骼独立参数设置
- 支持动画PNG帧序列导出
- 支持缩放旋转等导出画面设置
- 支持对独立的骨骼文件进行格式转换
- Coming soon...
## 使用方法 ## 使用方法
### 骨骼导入 ### 骨骼导入
**文件**菜单可以选择**打开**或者**批量打开**进行骨骼文件导入. 有 3 种模式导入骨骼文件:
或者直接把要打开的骨骼文件拖进列表, 这种方式只支持 `.json``.skel` 后缀的文件, 其他的会被忽略. - 拖放/粘贴需要导入的骨骼文件/目录到模型列表
### 骨骼调整 这种方式会自动查找传入的文件列表以及目录内的子级文件列表, 虽然方便但是依赖模型文件结构, 限制最多.
在**模型列表**中选择一项或多项, 将会在**模型参数**面板显示可供调节的参数. - 仅支持自动发现标准的 `*.json`/`*.skel`/`.atlas` 后缀文件.
- 骨骼文件和 atlas 文件需要是同名.
- 需要保证骨骼文件里的版本字符串不是魔改过的.
- 从文件菜单里批量打开骨骼文件
**模型列表**右键菜单可以对列表项进行增删调整, 也可以使用鼠标左键拖动调整顺序. 这种方式提供一定程度的文件自由度, 文件选择框里同样支持拖放/粘贴, 但是多一些额外选项.
### 画面调整 - 文件名限制条件与上面类似, 但是可以通过面板的选择文件按钮选择非标准后缀的骨骼文件.
- 可以设置固定加载版本, 便于应对魔改过的版本号.
- 选择单个模型打开
**预览画面**支持的鼠标操作: 这种方式自由度最高, 允许选择任意的骨骼文件和 atlas 文件, 可以没有文件名限制, 并且也可以设置加载版本.
- 左键可以对骨骼进行拖动 ### 预览内容调整
- 右键对画面进行拖动
- 滚轮进行画面缩放
除此之外, 也可以通过**画面参数**面板调节导出和预览时的画面参数. 模型列表支持右键菜单以及部分快捷键, 并且可以多选进行模型参数的批量调整.
在**功能**菜单中, 可以重置同步所有骨骼动画时间. 预览画面除了使用面板进行参数设置外, 支持部分鼠标动作:
### 动画导出 - 左键可以选择和拖拽模型, 按下 `Ctrl` 键可以实现多选, 与左侧列表选择是联动的.
- 右键对整体画面进行拖动.
- 滚轮进行画面缩放.
- 预览画面支持仅渲染选中, 在该模式下, 画面仅显示被选中的模型, 并且只能通过左侧列表改变选中状态.
**文件**菜单中选择**导出**可以将目前加载的所有骨骼动画按照预览时的画面进行PNG帧序列导出. 在功能菜单中, 可以重置同步所有骨骼动画时间.
可以在每个骨骼的**模型参数**中查看动画完整时长. ### 预览内容导出
支持预览图和视频的导出.
预览图导出的内容是模型的默认状态画面, 每个模型一张单独的预览图.
视频(TODO: 目前仅支持帧序列导出), 可以在每个骨骼的模型参数中查看动画完整时长.
当预览画面处于仅渲染选中状态时, 导出的内容仅包含被选中的模型, 也就是在画面中显示的内容.
### 格式与版本转换
可以通过工具菜单进行骨骼文件转换, 允许二进制和文本格式之间的转换, 以及不同版本间的转换.
目前处于施工中, 仅支持转换 `3.8.x` 二进制到文本格式.
--- ---

View File

@@ -0,0 +1,197 @@
namespace SpineViewer.Controls
{
partial class SkelFileListBox
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
tableLayoutPanel1 = new TableLayoutPanel();
flowLayoutPanel1 = new FlowLayoutPanel();
button_AddFolder = new Button();
button_AddFile = new Button();
label_Tip = new Label();
listBox = new ListBox();
contextMenuStrip = new ContextMenuStrip(components);
toolStripMenuItem_SelectAll = new ToolStripMenuItem();
toolStripMenuItem_Paste = new ToolStripMenuItem();
toolStripMenuItem_Remove = new ToolStripMenuItem();
folderBrowserDialog = new FolderBrowserDialog();
openFileDialog_Skel = new OpenFileDialog();
tableLayoutPanel1.SuspendLayout();
flowLayoutPanel1.SuspendLayout();
contextMenuStrip.SuspendLayout();
SuspendLayout();
//
// tableLayoutPanel1
//
tableLayoutPanel1.ColumnCount = 1;
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
tableLayoutPanel1.Controls.Add(flowLayoutPanel1, 0, 0);
tableLayoutPanel1.Controls.Add(listBox, 0, 1);
tableLayoutPanel1.Dock = DockStyle.Fill;
tableLayoutPanel1.Location = new Point(0, 0);
tableLayoutPanel1.Name = "tableLayoutPanel1";
tableLayoutPanel1.RowCount = 2;
tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
tableLayoutPanel1.Size = new Size(801, 394);
tableLayoutPanel1.TabIndex = 0;
//
// flowLayoutPanel1
//
flowLayoutPanel1.AutoSize = true;
flowLayoutPanel1.AutoSizeMode = AutoSizeMode.GrowAndShrink;
flowLayoutPanel1.Controls.Add(button_AddFolder);
flowLayoutPanel1.Controls.Add(button_AddFile);
flowLayoutPanel1.Controls.Add(label_Tip);
flowLayoutPanel1.Dock = DockStyle.Fill;
flowLayoutPanel1.Location = new Point(3, 3);
flowLayoutPanel1.Name = "flowLayoutPanel1";
flowLayoutPanel1.Size = new Size(795, 40);
flowLayoutPanel1.TabIndex = 1;
//
// button_AddFolder
//
button_AddFolder.AutoSize = true;
button_AddFolder.AutoSizeMode = AutoSizeMode.GrowAndShrink;
button_AddFolder.Location = new Point(3, 3);
button_AddFolder.Name = "button_AddFolder";
button_AddFolder.Size = new Size(122, 34);
button_AddFolder.TabIndex = 0;
button_AddFolder.Text = "添加文件夹...";
button_AddFolder.UseVisualStyleBackColor = true;
button_AddFolder.Click += button_AddFolder_Click;
//
// button_AddFile
//
button_AddFile.AutoSize = true;
button_AddFile.AutoSizeMode = AutoSizeMode.GrowAndShrink;
button_AddFile.Location = new Point(131, 3);
button_AddFile.Name = "button_AddFile";
button_AddFile.Size = new Size(104, 34);
button_AddFile.TabIndex = 1;
button_AddFile.Text = "添加文件...";
button_AddFile.UseVisualStyleBackColor = true;
button_AddFile.Click += button_AddFile_Click;
//
// label_Tip
//
label_Tip.Anchor = AnchorStyles.Left;
label_Tip.AutoSize = true;
label_Tip.Location = new Point(241, 8);
label_Tip.Name = "label_Tip";
label_Tip.Size = new Size(139, 24);
label_Tip.TabIndex = 3;
label_Tip.Text = "已添加 0 个文件";
label_Tip.TextAlign = ContentAlignment.MiddleCenter;
//
// listBox
//
listBox.AllowDrop = true;
listBox.ContextMenuStrip = contextMenuStrip;
listBox.Dock = DockStyle.Fill;
listBox.FormattingEnabled = true;
listBox.HorizontalScrollbar = true;
listBox.ItemHeight = 24;
listBox.Location = new Point(3, 49);
listBox.Name = "listBox";
listBox.SelectionMode = SelectionMode.MultiExtended;
listBox.Size = new Size(795, 342);
listBox.TabIndex = 0;
listBox.DragDrop += listBox_DragDrop;
listBox.DragEnter += listBox_DragEnter;
//
// contextMenuStrip
//
contextMenuStrip.ImageScalingSize = new Size(24, 24);
contextMenuStrip.Items.AddRange(new ToolStripItem[] { toolStripMenuItem_SelectAll, toolStripMenuItem_Paste, toolStripMenuItem_Remove });
contextMenuStrip.Name = "contextMenuStrip";
contextMenuStrip.Size = new Size(184, 94);
//
// toolStripMenuItem_SelectAll
//
toolStripMenuItem_SelectAll.Name = "toolStripMenuItem_SelectAll";
toolStripMenuItem_SelectAll.ShortcutKeys = Keys.Control | Keys.A;
toolStripMenuItem_SelectAll.Size = new Size(183, 30);
toolStripMenuItem_SelectAll.Text = "全选";
toolStripMenuItem_SelectAll.Click += toolStripMenuItem_SelectAll_Click;
//
// toolStripMenuItem_Paste
//
toolStripMenuItem_Paste.Name = "toolStripMenuItem_Paste";
toolStripMenuItem_Paste.ShortcutKeys = Keys.Control | Keys.V;
toolStripMenuItem_Paste.Size = new Size(183, 30);
toolStripMenuItem_Paste.Text = "粘贴";
toolStripMenuItem_Paste.Click += toolStripMenuItem_Paste_Click;
//
// toolStripMenuItem_Remove
//
toolStripMenuItem_Remove.Name = "toolStripMenuItem_Remove";
toolStripMenuItem_Remove.ShortcutKeys = Keys.Delete;
toolStripMenuItem_Remove.Size = new Size(183, 30);
toolStripMenuItem_Remove.Text = "移除";
toolStripMenuItem_Remove.Click += toolStripMenuItem_Remove_Click;
//
// openFileDialog_Skel
//
openFileDialog_Skel.AddExtension = false;
openFileDialog_Skel.AddToRecent = false;
openFileDialog_Skel.Filter = "skel 文件 (*.skel; *.json)|*.skel;*.json|二进制文件 (*.skel)|*.skel|文本文件 (*.json)|*.json|所有文件 (*.*)|*.*";
openFileDialog_Skel.Multiselect = true;
openFileDialog_Skel.Title = "批量选择skel文件";
//
// SkelFileListBox
//
AutoScaleDimensions = new SizeF(11F, 24F);
AutoScaleMode = AutoScaleMode.Font;
Controls.Add(tableLayoutPanel1);
Name = "SkelFileListBox";
Size = new Size(801, 394);
tableLayoutPanel1.ResumeLayout(false);
tableLayoutPanel1.PerformLayout();
flowLayoutPanel1.ResumeLayout(false);
flowLayoutPanel1.PerformLayout();
contextMenuStrip.ResumeLayout(false);
ResumeLayout(false);
}
#endregion
private TableLayoutPanel tableLayoutPanel1;
private ListBox listBox;
private FlowLayoutPanel flowLayoutPanel1;
private Button button_AddFolder;
private Button button_AddFile;
private FolderBrowserDialog folderBrowserDialog;
private Label label_Tip;
private ContextMenuStrip contextMenuStrip;
private OpenFileDialog openFileDialog_Skel;
private ToolStripMenuItem toolStripMenuItem_SelectAll;
private ToolStripMenuItem toolStripMenuItem_Paste;
private ToolStripMenuItem toolStripMenuItem_Remove;
}
}

View File

@@ -0,0 +1,123 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
namespace SpineViewer.Controls
{
public partial class SkelFileListBox : UserControl
{
public SkelFileListBox()
{
InitializeComponent();
Items = listBox.Items;
}
/// <summary>
/// ListBox.Items
/// </summary>
public readonly ListBox.ObjectCollection Items;
/// <summary>
/// 从路径列表添加
/// </summary>
private void AddFromFileDrop(string[] paths)
{
foreach (var path in paths)
{
if (File.Exists(path))
{
if (Spine.Spine.CommonSkelSuffix.Contains(Path.GetExtension(path).ToLower()))
listBox.Items.Add(Path.GetFullPath(path));
}
else if (Directory.Exists(path))
{
foreach (var file in Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories))
{
if (Spine.Spine.CommonSkelSuffix.Contains(Path.GetExtension(file).ToLower()))
listBox.Items.Add(file);
}
}
}
}
private void button_AddFolder_Click(object sender, EventArgs e)
{
if (folderBrowserDialog.ShowDialog() != DialogResult.OK)
return;
var path = folderBrowserDialog.SelectedPath;
if (Directory.Exists(path))
{
foreach (var file in Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories))
{
if (Spine.Spine.CommonSkelSuffix.Contains(Path.GetExtension(file).ToLower()))
listBox.Items.Add(file);
}
}
label_Tip.Text = $"已选择 {listBox.Items.Count} 个文件";
}
private void button_AddFile_Click(object sender, EventArgs e)
{
if (openFileDialog_Skel.ShowDialog() != DialogResult.OK)
return;
foreach (var p in openFileDialog_Skel.FileNames)
listBox.Items.Add(Path.GetFullPath(p));
label_Tip.Text = $"已选择 {listBox.Items.Count} 个文件";
}
private void listBox_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
e.Effect = DragDropEffects.Copy;
else
e.Effect = DragDropEffects.None;
}
private void listBox_DragDrop(object sender, DragEventArgs e)
{
if (!e.Data.GetDataPresent(DataFormats.FileDrop))
return;
AddFromFileDrop((string[])e.Data.GetData(DataFormats.FileDrop));
label_Tip.Text = $"已选择 {listBox.Items.Count} 个文件";
}
private void toolStripMenuItem_SelectAll_Click(object sender, EventArgs e)
{
for (int i = 0; i < listBox.Items.Count; i++)
listBox.SelectedIndices.Add(i);
}
private void toolStripMenuItem_Paste_Click(object sender, EventArgs e)
{
if (!Clipboard.ContainsFileDropList())
return;
var fileDropList = Clipboard.GetFileDropList();
var paths = new string[fileDropList.Count];
fileDropList.CopyTo(paths, 0);
AddFromFileDrop(paths);
label_Tip.Text = $"已选择 {listBox.Items.Count} 个文件";
}
private void toolStripMenuItem_Remove_Click(object sender, EventArgs e)
{
var indices = new int[listBox.SelectedIndices.Count];
listBox.SelectedIndices.CopyTo(indices, 0);
for (int i = indices.Length - 1; i >= 0; i--)
listBox.Items.RemoveAt(indices[i]);
label_Tip.Text = $"已选择 {listBox.Items.Count} 个文件";
}
}
}

View File

@@ -0,0 +1,129 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="contextMenuStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>556, 18</value>
</metadata>
<metadata name="folderBrowserDialog.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>286, 21</value>
</metadata>
<metadata name="openFileDialog_Skel.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>31, 27</value>
</metadata>
</root>

View File

@@ -37,10 +37,7 @@
tableLayoutPanel2 = new TableLayoutPanel(); tableLayoutPanel2 = new TableLayoutPanel();
button_Ok = new Button(); button_Ok = new Button();
button_Cancel = new Button(); button_Cancel = new Button();
listBox_FilePath = new ListBox(); skelFileListBox = new SpineViewer.Controls.SkelFileListBox();
button_SelectSkel = new Button();
label_Tip = new Label();
openFileDialog_Skel = new OpenFileDialog();
panel.SuspendLayout(); panel.SuspendLayout();
tableLayoutPanel1.SuspendLayout(); tableLayoutPanel1.SuspendLayout();
tableLayoutPanel2.SuspendLayout(); tableLayoutPanel2.SuspendLayout();
@@ -53,7 +50,7 @@
panel.Location = new Point(0, 0); panel.Location = new Point(0, 0);
panel.Name = "panel"; panel.Name = "panel";
panel.Padding = new Padding(50, 15, 50, 10); panel.Padding = new Padding(50, 15, 50, 10);
panel.Size = new Size(1126, 449); panel.Size = new Size(1042, 472);
panel.TabIndex = 1; panel.TabIndex = 1;
// //
// tableLayoutPanel1 // tableLayoutPanel1
@@ -62,22 +59,19 @@
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle()); tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle());
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F)); tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
tableLayoutPanel1.Controls.Add(label4, 0, 0); tableLayoutPanel1.Controls.Add(label4, 0, 0);
tableLayoutPanel1.Controls.Add(label3, 0, 3); tableLayoutPanel1.Controls.Add(label3, 0, 2);
tableLayoutPanel1.Controls.Add(comboBox_Version, 1, 3); tableLayoutPanel1.Controls.Add(comboBox_Version, 1, 2);
tableLayoutPanel1.Controls.Add(tableLayoutPanel2, 0, 4); tableLayoutPanel1.Controls.Add(tableLayoutPanel2, 0, 3);
tableLayoutPanel1.Controls.Add(listBox_FilePath, 0, 2); tableLayoutPanel1.Controls.Add(skelFileListBox, 0, 1);
tableLayoutPanel1.Controls.Add(button_SelectSkel, 0, 1);
tableLayoutPanel1.Controls.Add(label_Tip, 1, 1);
tableLayoutPanel1.Dock = DockStyle.Fill; tableLayoutPanel1.Dock = DockStyle.Fill;
tableLayoutPanel1.Location = new Point(50, 15); tableLayoutPanel1.Location = new Point(50, 15);
tableLayoutPanel1.Name = "tableLayoutPanel1"; tableLayoutPanel1.Name = "tableLayoutPanel1";
tableLayoutPanel1.RowCount = 5; tableLayoutPanel1.RowCount = 3;
tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
tableLayoutPanel1.RowStyles.Add(new RowStyle()); tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.RowStyles.Add(new RowStyle()); tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.RowStyles.Add(new RowStyle()); tableLayoutPanel1.Size = new Size(942, 447);
tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.Size = new Size(1026, 424);
tableLayoutPanel1.TabIndex = 1; tableLayoutPanel1.TabIndex = 1;
// //
// label4 // label4
@@ -88,7 +82,7 @@
label4.Location = new Point(15, 15); label4.Location = new Point(15, 15);
label4.Margin = new Padding(15); label4.Margin = new Padding(15);
label4.Name = "label4"; label4.Name = "label4";
label4.Size = new Size(996, 24); label4.Size = new Size(912, 24);
label4.TabIndex = 14; label4.TabIndex = 14;
label4.Text = "说明批量导入只需要选择skel文件atlas文件需要在同目录下并且与skel文件名相同"; label4.Text = "说明批量导入只需要选择skel文件atlas文件需要在同目录下并且与skel文件名相同";
label4.TextAlign = ContentAlignment.MiddleCenter; label4.TextAlign = ContentAlignment.MiddleCenter;
@@ -97,7 +91,7 @@
// //
label3.Anchor = AnchorStyles.Right; label3.Anchor = AnchorStyles.Right;
label3.AutoSize = true; label3.AutoSize = true;
label3.Location = new Point(90, 307); label3.Location = new Point(3, 343);
label3.Name = "label3"; label3.Name = "label3";
label3.Size = new Size(50, 24); label3.Size = new Size(50, 24);
label3.TabIndex = 12; label3.TabIndex = 12;
@@ -108,7 +102,7 @@
comboBox_Version.Anchor = AnchorStyles.Left; comboBox_Version.Anchor = AnchorStyles.Left;
comboBox_Version.DropDownStyle = ComboBoxStyle.DropDownList; comboBox_Version.DropDownStyle = ComboBoxStyle.DropDownList;
comboBox_Version.FormattingEnabled = true; comboBox_Version.FormattingEnabled = true;
comboBox_Version.Location = new Point(146, 303); comboBox_Version.Location = new Point(59, 339);
comboBox_Version.Name = "comboBox_Version"; comboBox_Version.Name = "comboBox_Version";
comboBox_Version.Size = new Size(182, 32); comboBox_Version.Size = new Size(182, 32);
comboBox_Version.Sorted = true; comboBox_Version.Sorted = true;
@@ -124,18 +118,19 @@
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_Ok, 0, 0);
tableLayoutPanel2.Controls.Add(button_Cancel, 1, 0); tableLayoutPanel2.Controls.Add(button_Cancel, 1, 0);
tableLayoutPanel2.Dock = DockStyle.Bottom; tableLayoutPanel2.Dock = DockStyle.Fill;
tableLayoutPanel2.Location = new Point(3, 381); tableLayoutPanel2.Location = new Point(3, 404);
tableLayoutPanel2.Margin = new Padding(3, 30, 3, 3);
tableLayoutPanel2.Name = "tableLayoutPanel2"; tableLayoutPanel2.Name = "tableLayoutPanel2";
tableLayoutPanel2.RowCount = 1; tableLayoutPanel2.RowCount = 1;
tableLayoutPanel2.RowStyles.Add(new RowStyle()); tableLayoutPanel2.RowStyles.Add(new RowStyle());
tableLayoutPanel2.Size = new Size(1020, 40); tableLayoutPanel2.Size = new Size(936, 40);
tableLayoutPanel2.TabIndex = 11; tableLayoutPanel2.TabIndex = 11;
// //
// button_Ok // button_Ok
// //
button_Ok.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; button_Ok.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
button_Ok.Location = new Point(368, 3); button_Ok.Location = new Point(326, 3);
button_Ok.Margin = new Padding(3, 3, 30, 3); button_Ok.Margin = new Padding(3, 3, 30, 3);
button_Ok.Name = "button_Ok"; button_Ok.Name = "button_Ok";
button_Ok.Size = new Size(112, 34); button_Ok.Size = new Size(112, 34);
@@ -147,7 +142,7 @@
// button_Cancel // button_Cancel
// //
button_Cancel.Anchor = AnchorStyles.Bottom | AnchorStyles.Left; button_Cancel.Anchor = AnchorStyles.Bottom | AnchorStyles.Left;
button_Cancel.Location = new Point(540, 3); button_Cancel.Location = new Point(498, 3);
button_Cancel.Margin = new Padding(30, 3, 3, 3); button_Cancel.Margin = new Padding(30, 3, 3, 3);
button_Cancel.Name = "button_Cancel"; button_Cancel.Name = "button_Cancel";
button_Cancel.Size = new Size(112, 34); button_Cancel.Size = new Size(112, 34);
@@ -156,47 +151,14 @@
button_Cancel.UseVisualStyleBackColor = true; button_Cancel.UseVisualStyleBackColor = true;
button_Cancel.Click += button_Cancel_Click; button_Cancel.Click += button_Cancel_Click;
// //
// listBox_FilePath // skelFileListBox
// //
tableLayoutPanel1.SetColumnSpan(listBox_FilePath, 2); tableLayoutPanel1.SetColumnSpan(skelFileListBox, 2);
listBox_FilePath.Dock = DockStyle.Fill; skelFileListBox.Dock = DockStyle.Fill;
listBox_FilePath.FormattingEnabled = true; skelFileListBox.Location = new Point(3, 57);
listBox_FilePath.HorizontalScrollbar = true; skelFileListBox.Name = "skelFileListBox";
listBox_FilePath.ItemHeight = 24; skelFileListBox.Size = new Size(936, 276);
listBox_FilePath.Location = new Point(3, 97); skelFileListBox.TabIndex = 15;
listBox_FilePath.Name = "listBox_FilePath";
listBox_FilePath.Size = new Size(1020, 200);
listBox_FilePath.TabIndex = 2;
//
// button_SelectSkel
//
button_SelectSkel.Anchor = AnchorStyles.None;
button_SelectSkel.Location = new Point(3, 57);
button_SelectSkel.Name = "button_SelectSkel";
button_SelectSkel.Size = new Size(137, 34);
button_SelectSkel.TabIndex = 1;
button_SelectSkel.Text = "选择文件...";
button_SelectSkel.UseVisualStyleBackColor = true;
button_SelectSkel.Click += button_SelectSkel_Click;
//
// label_Tip
//
label_Tip.AutoSize = true;
label_Tip.Dock = DockStyle.Fill;
label_Tip.Location = new Point(146, 54);
label_Tip.Name = "label_Tip";
label_Tip.Size = new Size(877, 40);
label_Tip.TabIndex = 0;
label_Tip.Text = "已选择 0 个文件";
label_Tip.TextAlign = ContentAlignment.MiddleLeft;
//
// openFileDialog_Skel
//
openFileDialog_Skel.AddExtension = false;
openFileDialog_Skel.AddToRecent = false;
openFileDialog_Skel.Filter = "skel 文件 (*.skel; *.json)|*.skel;*.json|二进制文件 (*.skel)|*.skel|文本文件 (*.json)|*.json|所有文件 (*.*)|*.*";
openFileDialog_Skel.Multiselect = true;
openFileDialog_Skel.Title = "批量选择skel文件";
// //
// BatchOpenSpineDialog // BatchOpenSpineDialog
// //
@@ -204,7 +166,7 @@
AutoScaleDimensions = new SizeF(11F, 24F); AutoScaleDimensions = new SizeF(11F, 24F);
AutoScaleMode = AutoScaleMode.Font; AutoScaleMode = AutoScaleMode.Font;
CancelButton = button_Cancel; CancelButton = button_Cancel;
ClientSize = new Size(1126, 449); ClientSize = new Size(1042, 472);
Controls.Add(panel); Controls.Add(panel);
FormBorderStyle = FormBorderStyle.FixedDialog; FormBorderStyle = FormBorderStyle.FixedDialog;
Icon = (Icon)resources.GetObject("$this.Icon"); Icon = (Icon)resources.GetObject("$this.Icon");
@@ -214,7 +176,6 @@
ShowInTaskbar = false; ShowInTaskbar = false;
StartPosition = FormStartPosition.CenterScreen; StartPosition = FormStartPosition.CenterScreen;
Text = "批量打开骨骼"; Text = "批量打开骨骼";
Load += BatchOpenSpineDialog_Load;
panel.ResumeLayout(false); panel.ResumeLayout(false);
tableLayoutPanel1.ResumeLayout(false); tableLayoutPanel1.ResumeLayout(false);
tableLayoutPanel1.PerformLayout(); tableLayoutPanel1.PerformLayout();
@@ -225,15 +186,12 @@
#endregion #endregion
private Panel panel; private Panel panel;
private TableLayoutPanel tableLayoutPanel1; private TableLayoutPanel tableLayoutPanel1;
private Label label_Tip;
private ListBox listBox_FilePath;
private Button button_SelectSkel;
private TableLayoutPanel tableLayoutPanel2; private TableLayoutPanel tableLayoutPanel2;
private Button button_Ok; private Button button_Ok;
private Button button_Cancel; private Button button_Cancel;
private Label label3; private Label label3;
private ComboBox comboBox_Version; private ComboBox comboBox_Version;
private OpenFileDialog openFileDialog_Skel;
private Label label4; private Label label4;
private Controls.SkelFileListBox skelFileListBox;
} }
} }

View File

@@ -27,33 +27,19 @@ namespace SpineViewer.Dialogs
comboBox_Version.SelectedValue = Spine.Version.Auto; comboBox_Version.SelectedValue = Spine.Version.Auto;
} }
private void BatchOpenSpineDialog_Load(object sender, EventArgs e)
{
button_SelectSkel_Click(sender, e);
}
private void button_SelectSkel_Click(object sender, EventArgs e)
{
if (openFileDialog_Skel.ShowDialog() == DialogResult.OK)
{
listBox_FilePath.Items.Clear();
foreach (var p in openFileDialog_Skel.FileNames)
listBox_FilePath.Items.Add(Path.GetFullPath(p));
label_Tip.Text = $"已选择 {listBox_FilePath.Items.Count} 个文件";
}
}
private void button_Ok_Click(object sender, EventArgs e) private void button_Ok_Click(object sender, EventArgs e)
{ {
var version = (Spine.Version)comboBox_Version.SelectedValue; var version = (Spine.Version)comboBox_Version.SelectedValue;
if (listBox_FilePath.Items.Count <= 0) var items = skelFileListBox.Items;
if (items.Count <= 0)
{ {
MessageBox.Info("未选择任何文件"); MessageBox.Info("未选择任何文件");
return; return;
} }
foreach (string p in listBox_FilePath.Items) foreach (string p in items)
{ {
if (!File.Exists(p)) if (!File.Exists(p))
{ {
@@ -68,7 +54,7 @@ namespace SpineViewer.Dialogs
return; return;
} }
Result = new(version, listBox_FilePath.Items.Cast<string>().ToArray()); Result = new(version, items.Cast<string>().ToArray());
DialogResult = DialogResult.OK; DialogResult = DialogResult.OK;
} }

View File

@@ -117,9 +117,6 @@
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<metadata name="openFileDialog_Skel.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>92, 26</value>
</metadata>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>

View File

@@ -31,6 +31,7 @@
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ConvertFileFormatDialog)); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ConvertFileFormatDialog));
panel = new Panel(); panel = new Panel();
tableLayoutPanel1 = new TableLayoutPanel(); tableLayoutPanel1 = new TableLayoutPanel();
comboBox_TargetVersion = new ComboBox();
flowLayoutPanel_TargetFormat = new FlowLayoutPanel(); flowLayoutPanel_TargetFormat = new FlowLayoutPanel();
radioButton_BinaryTarget = new RadioButton(); radioButton_BinaryTarget = new RadioButton();
radioButton_JsonTarget = new RadioButton(); radioButton_JsonTarget = new RadioButton();
@@ -41,19 +42,13 @@
tableLayoutPanel2 = new TableLayoutPanel(); tableLayoutPanel2 = new TableLayoutPanel();
button_Ok = new Button(); button_Ok = new Button();
button_Cancel = new Button(); button_Cancel = new Button();
listBox_FilePath = new ListBox();
button_SelectSkel = new Button();
label_Tip = new Label();
label2 = new Label(); label2 = new Label();
flowLayoutPanel_SourceFormat = new FlowLayoutPanel(); skelFileListBox = new SpineViewer.Controls.SkelFileListBox();
radioButton_BinarySource = new RadioButton();
radioButton_JsonSource = new RadioButton();
openFileDialog_Skel = new OpenFileDialog(); openFileDialog_Skel = new OpenFileDialog();
panel.SuspendLayout(); panel.SuspendLayout();
tableLayoutPanel1.SuspendLayout(); tableLayoutPanel1.SuspendLayout();
flowLayoutPanel_TargetFormat.SuspendLayout(); flowLayoutPanel_TargetFormat.SuspendLayout();
tableLayoutPanel2.SuspendLayout(); tableLayoutPanel2.SuspendLayout();
flowLayoutPanel_SourceFormat.SuspendLayout();
SuspendLayout(); SuspendLayout();
// //
// panel // panel
@@ -63,7 +58,7 @@
panel.Location = new Point(0, 0); panel.Location = new Point(0, 0);
panel.Name = "panel"; panel.Name = "panel";
panel.Padding = new Padding(50, 15, 50, 10); panel.Padding = new Padding(50, 15, 50, 10);
panel.Size = new Size(1039, 530); panel.Size = new Size(1051, 538);
panel.TabIndex = 2; panel.TabIndex = 2;
// //
// tableLayoutPanel1 // tableLayoutPanel1
@@ -71,40 +66,48 @@
tableLayoutPanel1.ColumnCount = 2; tableLayoutPanel1.ColumnCount = 2;
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle()); tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle());
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F)); tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
tableLayoutPanel1.Controls.Add(flowLayoutPanel_TargetFormat, 1, 5); tableLayoutPanel1.Controls.Add(comboBox_TargetVersion, 1, 3);
tableLayoutPanel1.Controls.Add(label1, 0, 4); tableLayoutPanel1.Controls.Add(flowLayoutPanel_TargetFormat, 1, 4);
tableLayoutPanel1.Controls.Add(label1, 0, 3);
tableLayoutPanel1.Controls.Add(label4, 0, 0); tableLayoutPanel1.Controls.Add(label4, 0, 0);
tableLayoutPanel1.Controls.Add(label3, 0, 3); tableLayoutPanel1.Controls.Add(label3, 0, 2);
tableLayoutPanel1.Controls.Add(comboBox_SourceVersion, 1, 3); tableLayoutPanel1.Controls.Add(comboBox_SourceVersion, 1, 2);
tableLayoutPanel1.Controls.Add(tableLayoutPanel2, 0, 6); tableLayoutPanel1.Controls.Add(tableLayoutPanel2, 0, 5);
tableLayoutPanel1.Controls.Add(listBox_FilePath, 0, 2); tableLayoutPanel1.Controls.Add(label2, 0, 4);
tableLayoutPanel1.Controls.Add(button_SelectSkel, 0, 1); tableLayoutPanel1.Controls.Add(skelFileListBox, 0, 1);
tableLayoutPanel1.Controls.Add(label_Tip, 1, 1);
tableLayoutPanel1.Controls.Add(label2, 0, 5);
tableLayoutPanel1.Controls.Add(flowLayoutPanel_SourceFormat, 1, 4);
tableLayoutPanel1.Dock = DockStyle.Fill; tableLayoutPanel1.Dock = DockStyle.Fill;
tableLayoutPanel1.Location = new Point(50, 15); tableLayoutPanel1.Location = new Point(50, 15);
tableLayoutPanel1.Name = "tableLayoutPanel1"; tableLayoutPanel1.Name = "tableLayoutPanel1";
tableLayoutPanel1.RowCount = 7; tableLayoutPanel1.RowCount = 6;
tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
tableLayoutPanel1.RowStyles.Add(new RowStyle()); tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.RowStyles.Add(new RowStyle()); tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.RowStyles.Add(new RowStyle()); tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.RowStyles.Add(new RowStyle()); tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.RowStyles.Add(new RowStyle()); tableLayoutPanel1.Size = new Size(951, 513);
tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.Size = new Size(939, 505);
tableLayoutPanel1.TabIndex = 1; tableLayoutPanel1.TabIndex = 1;
// //
// comboBox_TargetVersion
//
comboBox_TargetVersion.Anchor = AnchorStyles.Left;
comboBox_TargetVersion.DropDownStyle = ComboBoxStyle.DropDownList;
comboBox_TargetVersion.FormattingEnabled = true;
comboBox_TargetVersion.Location = new Point(95, 365);
comboBox_TargetVersion.Name = "comboBox_TargetVersion";
comboBox_TargetVersion.Size = new Size(182, 32);
comboBox_TargetVersion.Sorted = true;
comboBox_TargetVersion.TabIndex = 21;
//
// flowLayoutPanel_TargetFormat // flowLayoutPanel_TargetFormat
// //
flowLayoutPanel_TargetFormat.AutoSize = true; flowLayoutPanel_TargetFormat.AutoSize = true;
flowLayoutPanel_TargetFormat.Controls.Add(radioButton_BinaryTarget); flowLayoutPanel_TargetFormat.Controls.Add(radioButton_BinaryTarget);
flowLayoutPanel_TargetFormat.Controls.Add(radioButton_JsonTarget); flowLayoutPanel_TargetFormat.Controls.Add(radioButton_JsonTarget);
flowLayoutPanel_TargetFormat.Dock = DockStyle.Fill; flowLayoutPanel_TargetFormat.Dock = DockStyle.Fill;
flowLayoutPanel_TargetFormat.Location = new Point(146, 381); flowLayoutPanel_TargetFormat.Location = new Point(95, 403);
flowLayoutPanel_TargetFormat.Name = "flowLayoutPanel_TargetFormat"; flowLayoutPanel_TargetFormat.Name = "flowLayoutPanel_TargetFormat";
flowLayoutPanel_TargetFormat.Size = new Size(790, 34); flowLayoutPanel_TargetFormat.Size = new Size(853, 34);
flowLayoutPanel_TargetFormat.TabIndex = 19; flowLayoutPanel_TargetFormat.TabIndex = 19;
// //
// radioButton_BinaryTarget // radioButton_BinaryTarget
@@ -116,7 +119,6 @@
radioButton_BinaryTarget.TabIndex = 17; radioButton_BinaryTarget.TabIndex = 17;
radioButton_BinaryTarget.Text = "二进制 (*.skel)"; radioButton_BinaryTarget.Text = "二进制 (*.skel)";
radioButton_BinaryTarget.UseVisualStyleBackColor = true; radioButton_BinaryTarget.UseVisualStyleBackColor = true;
radioButton_BinaryTarget.CheckedChanged += radioButton_Target_CheckedChanged;
// //
// radioButton_JsonTarget // radioButton_JsonTarget
// //
@@ -129,17 +131,16 @@
radioButton_JsonTarget.TabStop = true; radioButton_JsonTarget.TabStop = true;
radioButton_JsonTarget.Text = "文本 (*.json)"; radioButton_JsonTarget.Text = "文本 (*.json)";
radioButton_JsonTarget.UseVisualStyleBackColor = true; radioButton_JsonTarget.UseVisualStyleBackColor = true;
radioButton_JsonTarget.CheckedChanged += radioButton_Target_CheckedChanged;
// //
// label1 // label1
// //
label1.Anchor = AnchorStyles.Right; label1.Anchor = AnchorStyles.Right;
label1.AutoSize = true; label1.AutoSize = true;
label1.Location = new Point(72, 346); label1.Location = new Point(3, 369);
label1.Name = "label1"; label1.Name = "label1";
label1.Size = new Size(68, 24); label1.Size = new Size(86, 24);
label1.TabIndex = 15; label1.TabIndex = 15;
label1.Text = "源格式:"; label1.Text = "目标版本:";
// //
// label4 // label4
// //
@@ -149,7 +150,7 @@
label4.Location = new Point(15, 15); label4.Location = new Point(15, 15);
label4.Margin = new Padding(15); label4.Margin = new Padding(15);
label4.Name = "label4"; label4.Name = "label4";
label4.Size = new Size(909, 24); label4.Size = new Size(921, 24);
label4.TabIndex = 14; label4.TabIndex = 14;
label4.Text = "说明:将在每个文件同级目录下生成目标格式后缀的文件,会覆盖已存在文件"; label4.Text = "说明:将在每个文件同级目录下生成目标格式后缀的文件,会覆盖已存在文件";
label4.TextAlign = ContentAlignment.MiddleCenter; label4.TextAlign = ContentAlignment.MiddleCenter;
@@ -158,19 +159,19 @@
// //
label3.Anchor = AnchorStyles.Right; label3.Anchor = AnchorStyles.Right;
label3.AutoSize = true; label3.AutoSize = true;
label3.Location = new Point(72, 307); label3.Location = new Point(21, 331);
label3.Name = "label3"; label3.Name = "label3";
label3.Size = new Size(68, 24); label3.Size = new Size(68, 24);
label3.TabIndex = 12; label3.TabIndex = 12;
label3.Text = "源版本:"; label3.Text = "源版本:";
// //
// comboBox_Version // comboBox_SourceVersion
// //
comboBox_SourceVersion.Anchor = AnchorStyles.Left; comboBox_SourceVersion.Anchor = AnchorStyles.Left;
comboBox_SourceVersion.DropDownStyle = ComboBoxStyle.DropDownList; comboBox_SourceVersion.DropDownStyle = ComboBoxStyle.DropDownList;
comboBox_SourceVersion.FormattingEnabled = true; comboBox_SourceVersion.FormattingEnabled = true;
comboBox_SourceVersion.Location = new Point(146, 303); comboBox_SourceVersion.Location = new Point(95, 327);
comboBox_SourceVersion.Name = "comboBox_Version"; comboBox_SourceVersion.Name = "comboBox_SourceVersion";
comboBox_SourceVersion.Size = new Size(182, 32); comboBox_SourceVersion.Size = new Size(182, 32);
comboBox_SourceVersion.Sorted = true; comboBox_SourceVersion.Sorted = true;
comboBox_SourceVersion.TabIndex = 13; comboBox_SourceVersion.TabIndex = 13;
@@ -185,18 +186,19 @@
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_Ok, 0, 0);
tableLayoutPanel2.Controls.Add(button_Cancel, 1, 0); tableLayoutPanel2.Controls.Add(button_Cancel, 1, 0);
tableLayoutPanel2.Dock = DockStyle.Bottom; tableLayoutPanel2.Dock = DockStyle.Fill;
tableLayoutPanel2.Location = new Point(3, 462); tableLayoutPanel2.Location = new Point(3, 470);
tableLayoutPanel2.Margin = new Padding(3, 30, 3, 3);
tableLayoutPanel2.Name = "tableLayoutPanel2"; tableLayoutPanel2.Name = "tableLayoutPanel2";
tableLayoutPanel2.RowCount = 1; tableLayoutPanel2.RowCount = 1;
tableLayoutPanel2.RowStyles.Add(new RowStyle()); tableLayoutPanel2.RowStyles.Add(new RowStyle());
tableLayoutPanel2.Size = new Size(933, 40); tableLayoutPanel2.Size = new Size(945, 40);
tableLayoutPanel2.TabIndex = 11; tableLayoutPanel2.TabIndex = 11;
// //
// button_Ok // button_Ok
// //
button_Ok.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; button_Ok.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
button_Ok.Location = new Point(324, 3); button_Ok.Location = new Point(330, 3);
button_Ok.Margin = new Padding(3, 3, 30, 3); button_Ok.Margin = new Padding(3, 3, 30, 3);
button_Ok.Name = "button_Ok"; button_Ok.Name = "button_Ok";
button_Ok.Size = new Size(112, 34); button_Ok.Size = new Size(112, 34);
@@ -208,7 +210,7 @@
// button_Cancel // button_Cancel
// //
button_Cancel.Anchor = AnchorStyles.Bottom | AnchorStyles.Left; button_Cancel.Anchor = AnchorStyles.Bottom | AnchorStyles.Left;
button_Cancel.Location = new Point(496, 3); button_Cancel.Location = new Point(502, 3);
button_Cancel.Margin = new Padding(30, 3, 3, 3); button_Cancel.Margin = new Padding(30, 3, 3, 3);
button_Cancel.Name = "button_Cancel"; button_Cancel.Name = "button_Cancel";
button_Cancel.Size = new Size(112, 34); button_Cancel.Size = new Size(112, 34);
@@ -217,84 +219,24 @@
button_Cancel.UseVisualStyleBackColor = true; button_Cancel.UseVisualStyleBackColor = true;
button_Cancel.Click += button_Cancel_Click; button_Cancel.Click += button_Cancel_Click;
// //
// listBox_FilePath
//
tableLayoutPanel1.SetColumnSpan(listBox_FilePath, 2);
listBox_FilePath.Dock = DockStyle.Fill;
listBox_FilePath.FormattingEnabled = true;
listBox_FilePath.HorizontalScrollbar = true;
listBox_FilePath.ItemHeight = 24;
listBox_FilePath.Location = new Point(3, 97);
listBox_FilePath.Name = "listBox_FilePath";
listBox_FilePath.Size = new Size(933, 200);
listBox_FilePath.TabIndex = 2;
//
// button_SelectSkel
//
button_SelectSkel.Anchor = AnchorStyles.None;
button_SelectSkel.Location = new Point(3, 57);
button_SelectSkel.Name = "button_SelectSkel";
button_SelectSkel.Size = new Size(137, 34);
button_SelectSkel.TabIndex = 1;
button_SelectSkel.Text = "选择文件...";
button_SelectSkel.UseVisualStyleBackColor = true;
button_SelectSkel.Click += button_SelectSkel_Click;
//
// label_Tip
//
label_Tip.AutoSize = true;
label_Tip.Dock = DockStyle.Fill;
label_Tip.Location = new Point(146, 54);
label_Tip.Name = "label_Tip";
label_Tip.Size = new Size(790, 40);
label_Tip.TabIndex = 0;
label_Tip.Text = "已选择 0 个文件";
label_Tip.TextAlign = ContentAlignment.MiddleLeft;
//
// label2 // label2
// //
label2.Anchor = AnchorStyles.Right; label2.Anchor = AnchorStyles.Right;
label2.AutoSize = true; label2.AutoSize = true;
label2.Location = new Point(54, 386); label2.Location = new Point(3, 408);
label2.Name = "label2"; label2.Name = "label2";
label2.Size = new Size(86, 24); label2.Size = new Size(86, 24);
label2.TabIndex = 16; label2.TabIndex = 16;
label2.Text = "目标格式:"; label2.Text = "目标格式:";
// //
// flowLayoutPanel_SourceFormat // skelFileListBox
// //
flowLayoutPanel_SourceFormat.AutoSize = true; tableLayoutPanel1.SetColumnSpan(skelFileListBox, 2);
flowLayoutPanel_SourceFormat.Controls.Add(radioButton_BinarySource); skelFileListBox.Dock = DockStyle.Fill;
flowLayoutPanel_SourceFormat.Controls.Add(radioButton_JsonSource); skelFileListBox.Location = new Point(3, 57);
flowLayoutPanel_SourceFormat.Dock = DockStyle.Fill; skelFileListBox.Name = "skelFileListBox";
flowLayoutPanel_SourceFormat.Location = new Point(146, 341); skelFileListBox.Size = new Size(945, 264);
flowLayoutPanel_SourceFormat.Name = "flowLayoutPanel_SourceFormat"; skelFileListBox.TabIndex = 20;
flowLayoutPanel_SourceFormat.Size = new Size(790, 34);
flowLayoutPanel_SourceFormat.TabIndex = 18;
//
// radioButton_BinarySource
//
radioButton_BinarySource.AutoSize = true;
radioButton_BinarySource.Checked = true;
radioButton_BinarySource.Location = new Point(3, 3);
radioButton_BinarySource.Name = "radioButton_BinarySource";
radioButton_BinarySource.Size = new Size(151, 28);
radioButton_BinarySource.TabIndex = 17;
radioButton_BinarySource.TabStop = true;
radioButton_BinarySource.Text = "二进制 (*.skel)";
radioButton_BinarySource.UseVisualStyleBackColor = true;
radioButton_BinarySource.CheckedChanged += radioButton_Source_CheckedChanged;
//
// radioButton_JsonSource
//
radioButton_JsonSource.AutoSize = true;
radioButton_JsonSource.Location = new Point(160, 3);
radioButton_JsonSource.Name = "radioButton_JsonSource";
radioButton_JsonSource.Size = new Size(135, 28);
radioButton_JsonSource.TabIndex = 18;
radioButton_JsonSource.Text = "文本 (*.json)";
radioButton_JsonSource.UseVisualStyleBackColor = true;
radioButton_JsonSource.CheckedChanged += radioButton_Source_CheckedChanged;
// //
// openFileDialog_Skel // openFileDialog_Skel
// //
@@ -310,7 +252,7 @@
AutoScaleDimensions = new SizeF(11F, 24F); AutoScaleDimensions = new SizeF(11F, 24F);
AutoScaleMode = AutoScaleMode.Font; AutoScaleMode = AutoScaleMode.Font;
CancelButton = button_Cancel; CancelButton = button_Cancel;
ClientSize = new Size(1039, 530); ClientSize = new Size(1051, 538);
Controls.Add(panel); Controls.Add(panel);
FormBorderStyle = FormBorderStyle.FixedDialog; FormBorderStyle = FormBorderStyle.FixedDialog;
Icon = (Icon)resources.GetObject("$this.Icon"); Icon = (Icon)resources.GetObject("$this.Icon");
@@ -320,15 +262,12 @@
ShowInTaskbar = false; ShowInTaskbar = false;
StartPosition = FormStartPosition.CenterScreen; StartPosition = FormStartPosition.CenterScreen;
Text = "骨骼文件格式转换"; Text = "骨骼文件格式转换";
Load += ConvertFileFormatDialog_Load;
panel.ResumeLayout(false); panel.ResumeLayout(false);
tableLayoutPanel1.ResumeLayout(false); tableLayoutPanel1.ResumeLayout(false);
tableLayoutPanel1.PerformLayout(); tableLayoutPanel1.PerformLayout();
flowLayoutPanel_TargetFormat.ResumeLayout(false); flowLayoutPanel_TargetFormat.ResumeLayout(false);
flowLayoutPanel_TargetFormat.PerformLayout(); flowLayoutPanel_TargetFormat.PerformLayout();
tableLayoutPanel2.ResumeLayout(false); tableLayoutPanel2.ResumeLayout(false);
flowLayoutPanel_SourceFormat.ResumeLayout(false);
flowLayoutPanel_SourceFormat.PerformLayout();
ResumeLayout(false); ResumeLayout(false);
} }
@@ -342,17 +281,13 @@
private TableLayoutPanel tableLayoutPanel2; private TableLayoutPanel tableLayoutPanel2;
private Button button_Ok; private Button button_Ok;
private Button button_Cancel; private Button button_Cancel;
private ListBox listBox_FilePath;
private Button button_SelectSkel;
private Label label_Tip;
private OpenFileDialog openFileDialog_Skel; private OpenFileDialog openFileDialog_Skel;
private Label label1; private Label label1;
private Label label2; private Label label2;
private RadioButton radioButton_BinarySource;
private FlowLayoutPanel flowLayoutPanel_SourceFormat;
private RadioButton radioButton_JsonSource;
private FlowLayoutPanel flowLayoutPanel_TargetFormat; private FlowLayoutPanel flowLayoutPanel_TargetFormat;
private RadioButton radioButton_BinaryTarget; private RadioButton radioButton_BinaryTarget;
private RadioButton radioButton_JsonTarget; private RadioButton radioButton_JsonTarget;
private Controls.SkelFileListBox skelFileListBox;
private ComboBox comboBox_TargetVersion;
} }
} }

View File

@@ -13,62 +13,44 @@ namespace SpineViewer.Dialogs
{ {
public partial class ConvertFileFormatDialog : Form public partial class ConvertFileFormatDialog : Form
{ {
// TODO: 增加版本转换选项 /// <summary>
// TODO: 使用结果包装类 /// 对话框结果, 取消时为 null
public string[] SkelPaths { get; private set; } /// </summary>
public Spine.Version SourceVersion { get; private set; } public ConvertFileFormatDialogResult Result { get; private set; }
public Spine.Version TargetVersion { get; private set; }
public bool JsonSource { get; private set; }
public bool JsonTarget { get; private set; }
public ConvertFileFormatDialog() public ConvertFileFormatDialog()
{ {
InitializeComponent(); InitializeComponent();
// XXX: 文件格式转换暂时不支持自动检测版本 comboBox_SourceVersion.DataSource = VersionHelper.Names.ToList();
var impVersions = VersionHelper.Names.ToDictionary();
impVersions.Remove(Spine.Version.Auto);
comboBox_SourceVersion.DataSource = impVersions.ToList();
comboBox_SourceVersion.DisplayMember = "Value"; comboBox_SourceVersion.DisplayMember = "Value";
comboBox_SourceVersion.ValueMember = "Key"; comboBox_SourceVersion.ValueMember = "Key";
comboBox_SourceVersion.SelectedValue = Spine.Version.V38; comboBox_SourceVersion.SelectedValue = Spine.Version.Auto;
//comboBox_TargetVersion.DataSource = VersionHelper.Versions.ToList();
//comboBox_TargetVersion.DisplayMember = "Value";
//comboBox_TargetVersion.ValueMember = "Key";
//comboBox_TargetVersion.SelectedValue = Spine.Version.V38;
}
private void ConvertFileFormatDialog_Load(object sender, EventArgs e) // 目标版本不包含自动
{ var versionsWithoutAuto = VersionHelper.Names.ToDictionary();
button_SelectSkel_Click(sender, e); versionsWithoutAuto.Remove(Spine.Version.Auto);
} comboBox_TargetVersion.DataSource = versionsWithoutAuto.ToList();
comboBox_TargetVersion.DisplayMember = "Value";
private void button_SelectSkel_Click(object sender, EventArgs e) comboBox_TargetVersion.ValueMember = "Key";
{ comboBox_TargetVersion.SelectedValue = Spine.Version.V38;
if (openFileDialog_Skel.ShowDialog() == DialogResult.OK)
{
listBox_FilePath.Items.Clear();
foreach (var p in openFileDialog_Skel.FileNames)
listBox_FilePath.Items.Add(Path.GetFullPath(p));
label_Tip.Text = $"已选择 {listBox_FilePath.Items.Count} 个文件";
}
} }
private void button_Ok_Click(object sender, EventArgs e) private void button_Ok_Click(object sender, EventArgs e)
{ {
var sourceVersion = (Spine.Version)comboBox_SourceVersion.SelectedValue; var sourceVersion = (Spine.Version)comboBox_SourceVersion.SelectedValue;
var targetVersion = (Spine.Version)comboBox_SourceVersion.SelectedValue; // TODO: 增加目标版本 var targetVersion = (Spine.Version)comboBox_TargetVersion.SelectedValue;
var jsonSource = radioButton_JsonSource.Checked;
var jsonTarget = radioButton_JsonTarget.Checked; var jsonTarget = radioButton_JsonTarget.Checked;
if (listBox_FilePath.Items.Count <= 0) var items = skelFileListBox.Items;
if (items.Count <= 0)
{ {
MessageBox.Info("未选择任何文件"); MessageBox.Info("未选择任何文件");
return; return;
} }
foreach (string p in listBox_FilePath.Items) foreach (string p in items)
{ {
if (!File.Exists(p)) if (!File.Exists(p))
{ {
@@ -77,7 +59,7 @@ namespace SpineViewer.Dialogs
} }
} }
if (!SkeletonConverter.ImplementedVersions.Contains(sourceVersion)) if (sourceVersion != Spine.Version.Auto && !SkeletonConverter.ImplementedVersions.Contains(sourceVersion))
{ {
MessageBox.Info($"{sourceVersion.GetName()} 版本尚未实现(咕咕咕~"); MessageBox.Info($"{sourceVersion.GetName()} 版本尚未实现(咕咕咕~");
return; return;
@@ -89,18 +71,7 @@ namespace SpineViewer.Dialogs
return; return;
} }
if (jsonSource == jsonTarget && sourceVersion == targetVersion) Result = new(items.Cast<string>().ToArray(), sourceVersion, targetVersion, jsonTarget);
{
MessageBox.Info($"不需要转换相同的格式和版本");
return;
}
SkelPaths = listBox_FilePath.Items.Cast<string>().ToArray();
SourceVersion = sourceVersion;
TargetVersion = targetVersion;
JsonSource = jsonSource;
JsonTarget = jsonTarget;
DialogResult = DialogResult.OK; DialogResult = DialogResult.OK;
} }
@@ -108,21 +79,31 @@ namespace SpineViewer.Dialogs
{ {
DialogResult = DialogResult.Cancel; DialogResult = DialogResult.Cancel;
} }
}
private void radioButton_Source_CheckedChanged(object sender, EventArgs e) /// <summary>
{ /// 文件格式转换对话框结果包装类
if (radioButton_BinarySource.Checked) /// </summary>
radioButton_JsonTarget.Checked = true; public class ConvertFileFormatDialogResult(string[] skelPaths, Spine.Version sourceVersion, Spine.Version targetVersion, bool jsonTarget)
else {
radioButton_BinaryTarget.Checked = true; /// <summary>
} /// 骨骼文件路径列表
/// </summary>
public string[] SkelPaths => skelPaths;
private void radioButton_Target_CheckedChanged(object sender, EventArgs e) /// <summary>
{ /// 源版本
if (radioButton_BinaryTarget.Checked) /// </summary>
radioButton_JsonSource.Checked = true; public Spine.Version SourceVersion => sourceVersion;
else
radioButton_BinarySource.Checked = true; /// <summary>
} /// 目标版本
/// </summary>
public Spine.Version TargetVersion => targetVersion;
/// <summary>
/// 目标格式是否为 Json
/// </summary>
public bool JsonTarget => jsonTarget;
} }
} }

View File

@@ -31,23 +31,18 @@
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ExportPreviewDialog)); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ExportPreviewDialog));
panel1 = new Panel(); panel1 = new Panel();
tableLayoutPanel1 = new TableLayoutPanel(); tableLayoutPanel1 = new TableLayoutPanel();
propertyGrid = new PropertyGrid();
label4 = new Label(); label4 = new Label();
label1 = new Label(); label1 = new Label();
label2 = new Label();
label3 = new Label();
textBox_OutputDir = new TextBox(); textBox_OutputDir = new TextBox();
button_SelectOutputDir = new Button(); button_SelectOutputDir = new Button();
tableLayoutPanel2 = new TableLayoutPanel(); tableLayoutPanel2 = new TableLayoutPanel();
button_Ok = new Button(); button_Ok = new Button();
button_Cancel = new Button(); button_Cancel = new Button();
numericUpDown_Width = new NumericUpDown();
numericUpDown_Height = new NumericUpDown();
folderBrowserDialog = new FolderBrowserDialog(); folderBrowserDialog = new FolderBrowserDialog();
panel1.SuspendLayout(); panel1.SuspendLayout();
tableLayoutPanel1.SuspendLayout(); tableLayoutPanel1.SuspendLayout();
tableLayoutPanel2.SuspendLayout(); tableLayoutPanel2.SuspendLayout();
((System.ComponentModel.ISupportInitialize)numericUpDown_Width).BeginInit();
((System.ComponentModel.ISupportInitialize)numericUpDown_Height).BeginInit();
SuspendLayout(); SuspendLayout();
// //
// panel1 // panel1
@@ -57,7 +52,7 @@
panel1.Location = new Point(0, 0); panel1.Location = new Point(0, 0);
panel1.Name = "panel1"; panel1.Name = "panel1";
panel1.Padding = new Padding(50, 15, 50, 10); panel1.Padding = new Padding(50, 15, 50, 10);
panel1.Size = new Size(919, 276); panel1.Size = new Size(914, 482);
panel1.TabIndex = 2; panel1.TabIndex = 2;
// //
// tableLayoutPanel1 // tableLayoutPanel1
@@ -68,27 +63,34 @@
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F)); tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F));
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F)); tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F));
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle()); tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle());
tableLayoutPanel1.Controls.Add(propertyGrid, 0, 2);
tableLayoutPanel1.Controls.Add(label4, 0, 0); tableLayoutPanel1.Controls.Add(label4, 0, 0);
tableLayoutPanel1.Controls.Add(label1, 0, 1); tableLayoutPanel1.Controls.Add(label1, 0, 1);
tableLayoutPanel1.Controls.Add(label2, 0, 2);
tableLayoutPanel1.Controls.Add(label3, 0, 3);
tableLayoutPanel1.Controls.Add(textBox_OutputDir, 1, 1); tableLayoutPanel1.Controls.Add(textBox_OutputDir, 1, 1);
tableLayoutPanel1.Controls.Add(button_SelectOutputDir, 3, 1); tableLayoutPanel1.Controls.Add(button_SelectOutputDir, 3, 1);
tableLayoutPanel1.Controls.Add(tableLayoutPanel2, 0, 4); tableLayoutPanel1.Controls.Add(tableLayoutPanel2, 0, 3);
tableLayoutPanel1.Controls.Add(numericUpDown_Width, 1, 2);
tableLayoutPanel1.Controls.Add(numericUpDown_Height, 1, 3);
tableLayoutPanel1.Dock = DockStyle.Fill; tableLayoutPanel1.Dock = DockStyle.Fill;
tableLayoutPanel1.Location = new Point(50, 15); tableLayoutPanel1.Location = new Point(50, 15);
tableLayoutPanel1.Name = "tableLayoutPanel1"; tableLayoutPanel1.Name = "tableLayoutPanel1";
tableLayoutPanel1.RowCount = 5; tableLayoutPanel1.RowCount = 4;
tableLayoutPanel1.RowStyles.Add(new RowStyle()); tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.RowStyles.Add(new RowStyle()); tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
tableLayoutPanel1.RowStyles.Add(new RowStyle()); tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.RowStyles.Add(new RowStyle()); tableLayoutPanel1.Size = new Size(814, 457);
tableLayoutPanel1.RowStyles.Add(new RowStyle());
tableLayoutPanel1.Size = new Size(819, 251);
tableLayoutPanel1.TabIndex = 0; tableLayoutPanel1.TabIndex = 0;
// //
// propertyGrid
//
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
// //
label4.AutoSize = true; label4.AutoSize = true;
@@ -97,9 +99,9 @@
label4.Location = new Point(15, 15); label4.Location = new Point(15, 15);
label4.Margin = new Padding(15); label4.Margin = new Padding(15);
label4.Name = "label4"; label4.Name = "label4";
label4.Size = new Size(789, 24); label4.Size = new Size(784, 24);
label4.TabIndex = 11; label4.TabIndex = 11;
label4.Text = "说明:导出的文件名与骨骼文件名相同"; label4.Text = "说明:输出文件夹为可选项,留空则将预览图输出到每个骨骼文件所在目录";
label4.TextAlign = ContentAlignment.MiddleCenter; label4.TextAlign = ContentAlignment.MiddleCenter;
// //
// label1 // label1
@@ -112,40 +114,20 @@
label1.TabIndex = 0; label1.TabIndex = 0;
label1.Text = "输出文件夹:"; label1.Text = "输出文件夹:";
// //
// label2
//
label2.Anchor = AnchorStyles.Right;
label2.AutoSize = true;
label2.Location = new Point(75, 100);
label2.Name = "label2";
label2.Size = new Size(32, 24);
label2.TabIndex = 1;
label2.Text = "宽:";
//
// label3
//
label3.Anchor = AnchorStyles.Right;
label3.AutoSize = true;
label3.Location = new Point(75, 136);
label3.Name = "label3";
label3.Size = new Size(32, 24);
label3.TabIndex = 2;
label3.Text = "高:";
//
// textBox_OutputDir // textBox_OutputDir
// //
tableLayoutPanel1.SetColumnSpan(textBox_OutputDir, 2); tableLayoutPanel1.SetColumnSpan(textBox_OutputDir, 2);
textBox_OutputDir.Dock = DockStyle.Fill; textBox_OutputDir.Dock = DockStyle.Fill;
textBox_OutputDir.Location = new Point(113, 57); textBox_OutputDir.Location = new Point(113, 57);
textBox_OutputDir.Name = "textBox_OutputDir"; textBox_OutputDir.Name = "textBox_OutputDir";
textBox_OutputDir.Size = new Size(664, 30); textBox_OutputDir.Size = new Size(660, 30);
textBox_OutputDir.TabIndex = 3; textBox_OutputDir.TabIndex = 3;
// //
// button_SelectOutputDir // button_SelectOutputDir
// //
button_SelectOutputDir.AutoSize = true; button_SelectOutputDir.AutoSize = true;
button_SelectOutputDir.AutoSizeMode = AutoSizeMode.GrowAndShrink; button_SelectOutputDir.AutoSizeMode = AutoSizeMode.GrowAndShrink;
button_SelectOutputDir.Location = new Point(783, 57); button_SelectOutputDir.Location = new Point(779, 57);
button_SelectOutputDir.Name = "button_SelectOutputDir"; button_SelectOutputDir.Name = "button_SelectOutputDir";
button_SelectOutputDir.Size = new Size(32, 34); button_SelectOutputDir.Size = new Size(32, 34);
button_SelectOutputDir.TabIndex = 5; button_SelectOutputDir.TabIndex = 5;
@@ -164,17 +146,18 @@
tableLayoutPanel2.Controls.Add(button_Ok, 0, 0); tableLayoutPanel2.Controls.Add(button_Ok, 0, 0);
tableLayoutPanel2.Controls.Add(button_Cancel, 1, 0); tableLayoutPanel2.Controls.Add(button_Cancel, 1, 0);
tableLayoutPanel2.Dock = DockStyle.Bottom; tableLayoutPanel2.Dock = DockStyle.Bottom;
tableLayoutPanel2.Location = new Point(3, 208); tableLayoutPanel2.Location = new Point(3, 414);
tableLayoutPanel2.Margin = new Padding(3, 30, 3, 3);
tableLayoutPanel2.Name = "tableLayoutPanel2"; tableLayoutPanel2.Name = "tableLayoutPanel2";
tableLayoutPanel2.RowCount = 1; tableLayoutPanel2.RowCount = 1;
tableLayoutPanel2.RowStyles.Add(new RowStyle()); tableLayoutPanel2.RowStyles.Add(new RowStyle());
tableLayoutPanel2.Size = new Size(813, 40); tableLayoutPanel2.Size = new Size(808, 40);
tableLayoutPanel2.TabIndex = 10; tableLayoutPanel2.TabIndex = 10;
// //
// button_Ok // button_Ok
// //
button_Ok.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; button_Ok.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
button_Ok.Location = new Point(264, 3); button_Ok.Location = new Point(262, 3);
button_Ok.Margin = new Padding(3, 3, 30, 3); button_Ok.Margin = new Padding(3, 3, 30, 3);
button_Ok.Name = "button_Ok"; button_Ok.Name = "button_Ok";
button_Ok.Size = new Size(112, 34); button_Ok.Size = new Size(112, 34);
@@ -186,7 +169,7 @@
// button_Cancel // button_Cancel
// //
button_Cancel.Anchor = AnchorStyles.Bottom | AnchorStyles.Left; button_Cancel.Anchor = AnchorStyles.Bottom | AnchorStyles.Left;
button_Cancel.Location = new Point(436, 3); button_Cancel.Location = new Point(434, 3);
button_Cancel.Margin = new Padding(30, 3, 3, 3); button_Cancel.Margin = new Padding(30, 3, 3, 3);
button_Cancel.Name = "button_Cancel"; button_Cancel.Name = "button_Cancel";
button_Cancel.Size = new Size(112, 34); button_Cancel.Size = new Size(112, 34);
@@ -195,30 +178,6 @@
button_Cancel.UseVisualStyleBackColor = true; button_Cancel.UseVisualStyleBackColor = true;
button_Cancel.Click += button_Cancel_Click; button_Cancel.Click += button_Cancel_Click;
// //
// numericUpDown_Width
//
numericUpDown_Width.Anchor = AnchorStyles.Left;
numericUpDown_Width.Location = new Point(113, 97);
numericUpDown_Width.Maximum = new decimal(new int[] { 4096, 0, 0, 0 });
numericUpDown_Width.Minimum = new decimal(new int[] { 32, 0, 0, 0 });
numericUpDown_Width.Name = "numericUpDown_Width";
numericUpDown_Width.Size = new Size(180, 30);
numericUpDown_Width.TabIndex = 12;
numericUpDown_Width.TextAlign = HorizontalAlignment.Right;
numericUpDown_Width.Value = new decimal(new int[] { 256, 0, 0, 0 });
//
// numericUpDown_Height
//
numericUpDown_Height.Anchor = AnchorStyles.Left;
numericUpDown_Height.Location = new Point(113, 133);
numericUpDown_Height.Maximum = new decimal(new int[] { 4096, 0, 0, 0 });
numericUpDown_Height.Minimum = new decimal(new int[] { 32, 0, 0, 0 });
numericUpDown_Height.Name = "numericUpDown_Height";
numericUpDown_Height.Size = new Size(180, 30);
numericUpDown_Height.TabIndex = 13;
numericUpDown_Height.TextAlign = HorizontalAlignment.Right;
numericUpDown_Height.Value = new decimal(new int[] { 256, 0, 0, 0 });
//
// folderBrowserDialog // folderBrowserDialog
// //
folderBrowserDialog.AddToRecent = false; folderBrowserDialog.AddToRecent = false;
@@ -229,7 +188,7 @@
AutoScaleDimensions = new SizeF(11F, 24F); AutoScaleDimensions = new SizeF(11F, 24F);
AutoScaleMode = AutoScaleMode.Font; AutoScaleMode = AutoScaleMode.Font;
CancelButton = button_Cancel; CancelButton = button_Cancel;
ClientSize = new Size(919, 276); ClientSize = new Size(914, 482);
Controls.Add(panel1); Controls.Add(panel1);
FormBorderStyle = FormBorderStyle.FixedDialog; FormBorderStyle = FormBorderStyle.FixedDialog;
Icon = (Icon)resources.GetObject("$this.Icon"); Icon = (Icon)resources.GetObject("$this.Icon");
@@ -239,14 +198,11 @@
ShowInTaskbar = false; ShowInTaskbar = false;
StartPosition = FormStartPosition.CenterScreen; StartPosition = FormStartPosition.CenterScreen;
Text = "导出预览图"; Text = "导出预览图";
Load += ExportPreviewDialog_Load;
panel1.ResumeLayout(false); panel1.ResumeLayout(false);
panel1.PerformLayout(); panel1.PerformLayout();
tableLayoutPanel1.ResumeLayout(false); tableLayoutPanel1.ResumeLayout(false);
tableLayoutPanel1.PerformLayout(); tableLayoutPanel1.PerformLayout();
tableLayoutPanel2.ResumeLayout(false); tableLayoutPanel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)numericUpDown_Width).EndInit();
((System.ComponentModel.ISupportInitialize)numericUpDown_Height).EndInit();
ResumeLayout(false); ResumeLayout(false);
} }
@@ -256,15 +212,12 @@
private TableLayoutPanel tableLayoutPanel1; private TableLayoutPanel tableLayoutPanel1;
private Label label4; private Label label4;
private Label label1; private Label label1;
private Label label2;
private Label label3;
private TextBox textBox_OutputDir; private TextBox textBox_OutputDir;
private Button button_SelectOutputDir; private Button button_SelectOutputDir;
private TableLayoutPanel tableLayoutPanel2; private TableLayoutPanel tableLayoutPanel2;
private Button button_Ok; private Button button_Ok;
private Button button_Cancel; private Button button_Cancel;
private NumericUpDown numericUpDown_Width;
private NumericUpDown numericUpDown_Height;
private FolderBrowserDialog folderBrowserDialog; private FolderBrowserDialog folderBrowserDialog;
private PropertyGrid propertyGrid;
} }
} }

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Data; using System.Data;
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -12,60 +13,66 @@ namespace SpineViewer.Dialogs
{ {
public partial class ExportPreviewDialog: Form public partial class ExportPreviewDialog: Form
{ {
// TODO: 用单独的结果包装类 /// <summary>
public string OutputDir { get; private set; } /// 对话框结果
public uint PreviewWidth { get; private set; } /// </summary>
public uint PreviewHeight { get; private set; } public readonly ExportPreviewDialogResult Result = new();
public ExportPreviewDialog() public ExportPreviewDialog()
{ {
InitializeComponent(); InitializeComponent();
} propertyGrid.SelectedObject = Result;
private void ExportPreviewDialog_Load(object sender, EventArgs e)
{
button_SelectOutputDir_Click(sender, e);
} }
private void button_SelectOutputDir_Click(object sender, EventArgs e) private void button_SelectOutputDir_Click(object sender, EventArgs e)
{ {
folderBrowserDialog.InitialDirectory = textBox_OutputDir.Text; folderBrowserDialog.InitialDirectory = textBox_OutputDir.Text;
if (folderBrowserDialog.ShowDialog() == DialogResult.OK) if (folderBrowserDialog.ShowDialog() != DialogResult.OK)
{ return;
textBox_OutputDir.Text = Path.GetFullPath(folderBrowserDialog.SelectedPath); textBox_OutputDir.Text = Path.GetFullPath(folderBrowserDialog.SelectedPath);
}
} }
private void button_Ok_Click(object sender, EventArgs e) private void button_Ok_Click(object sender, EventArgs e)
{ {
var outputDir = textBox_OutputDir.Text; var outputDir = textBox_OutputDir.Text;
if (File.Exists(outputDir)) if (string.IsNullOrEmpty(outputDir))
{ {
MessageBox.Info("输出文件夹无效"); 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; return;
} }
if (!Directory.Exists(outputDir)) Result.OutputDir = 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);
PreviewWidth = (uint)numericUpDown_Width.Value;
PreviewHeight = (uint)numericUpDown_Height.Value;
DialogResult = DialogResult.OK; DialogResult = DialogResult.OK;
} }
@@ -74,4 +81,89 @@ namespace SpineViewer.Dialogs
DialogResult = DialogResult.Cancel; DialogResult = DialogResult.Cancel;
} }
} }
public class ExportPreviewDialogResult
{
/// <summary>
/// 输出路径
/// </summary>
[Browsable(false)]
public string? OutputDir { get; set; } = null;
/// <summary>
/// 预览图格式
/// </summary>
[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;
/// <summary>
/// 预览图分辨率
/// </summary>
[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);
/// <summary>
/// 四周填充像素值
/// </summary>
[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);
/// <summary>
/// DPI
/// </summary>
[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);
/// <summary>
/// 名称后缀
/// </summary>
[Category("其他"), DisplayName("名称后缀")]
public string NameSuffix { get; set; } = "(preview)";
}
} }

View File

@@ -0,0 +1,64 @@
using FFMpegCore.Pipes;
using System;
using System.Collections.Generic;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.StartPanel;
namespace SpineViewer
{
/// <summary>
/// SFML.Graphics.Image 帧对象包装类
/// </summary>
public class SFMLImageVideoFrame(SFML.Graphics.Image image) : IVideoFrame, IDisposable
{
public int Width => (int)image.Size.X;
public int Height => (int)image.Size.Y;
public string Format => "rgba";
public void Serialize(Stream pipe) => pipe.Write(image.Pixels);
public async Task SerializeAsync(Stream pipe, CancellationToken token) => await pipe.WriteAsync(image.Pixels, token);
public void Dispose() => image.Dispose();
/// <summary>
/// Save the contents of the image to a file
/// </summary>
/// <param name="filename">Path of the file to save (overwritten if already exist)</param>
/// <returns>True if saving was successful</returns>
public bool SaveToFile(string filename) => image.SaveToFile(filename);
/// <summary>
/// Save the image to a buffer in memory The format of the image must be specified.
/// The supported image formats are bmp, png, tga and jpg. This function fails if
/// the image is empty, or if the format was invalid.
/// </summary>
/// <param name="output">Byte array filled with encoded data</param>
/// <param name="format">Encoding format to use</param>
/// <returns>True if saving was successful</returns>
public bool SaveToMemory(out byte[] output, string format) => image.SaveToMemory(out output, format);
}
/// <summary>
/// 为帧导出创建的辅助类
/// </summary>
public static class ExportHelper
{
public static Bitmap CopyToBitmap(this SFML.Graphics.Texture tex)
{
using var img = tex.CopyToImage();
img.SaveToMemory(out var imgBuffer, "bmp");
using var stream = new MemoryStream(imgBuffer);
return new Bitmap(stream);
}
public static SFMLImageVideoFrame CopyToFrame(this SFML.Graphics.Texture tex) => new(tex.CopyToImage());
public static string GetSuffix(this ImageFormat imageFormat)
{
if (imageFormat == ImageFormat.Icon) return ".ico";
else if (imageFormat == ImageFormat.Exif) return ".jpg";
else return $".{imageFormat.ToString().ToLower()}";
}
}
}

View File

@@ -106,7 +106,7 @@ namespace SpineViewer
var progressDialog = new Dialogs.ProgressDialog(); var progressDialog = new Dialogs.ProgressDialog();
progressDialog.DoWork += ExportPreview_Work; progressDialog.DoWork += ExportPreview_Work;
progressDialog.RunWorkerAsync(saveDialog); progressDialog.RunWorkerAsync(saveDialog.Result);
progressDialog.ShowDialog(); progressDialog.ShowDialog();
} }
@@ -132,7 +132,7 @@ namespace SpineViewer
var progressDialog = new Dialogs.ProgressDialog(); var progressDialog = new Dialogs.ProgressDialog();
progressDialog.DoWork += ConvertFileFormat_Work; progressDialog.DoWork += ConvertFileFormat_Work;
progressDialog.RunWorkerAsync(openDialog); progressDialog.RunWorkerAsync(openDialog.Result);
progressDialog.ShowDialog(); progressDialog.ShowDialog();
} }
@@ -294,17 +294,17 @@ namespace SpineViewer
private void ExportPreview_Work(object? sender, DoWorkEventArgs e) private void ExportPreview_Work(object? sender, DoWorkEventArgs e)
{ {
var worker = sender as BackgroundWorker; var worker = sender as BackgroundWorker;
var arguments = e.Argument as Dialogs.ExportPreviewDialog; var arguments = e.Argument as Dialogs.ExportPreviewDialogResult;
var outputDir = arguments.OutputDir;
var width = arguments.PreviewWidth;
var height = arguments.PreviewHeight;
// TODO: 增加填充参数
var paddingL = 1u;
var paddingR = 1u;
var paddingT = 1u;
var paddingB = 1u;
var tex = new SFML.Graphics.RenderTexture(width, height); 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);
int success = 0; int success = 0;
int error = 0; int error = 0;
@@ -323,18 +323,26 @@ namespace SpineViewer
} }
var spine = spines[i]; 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; var tmp = spine.CurrentAnimation;
spine.CurrentAnimation = Spine.Spine.EMPTY_ANIMATION; spine.CurrentAnimation = Spine.Spine.EMPTY_ANIMATION;
tex.SetView(spine.GetInitView(width, height, paddingL, paddingR, paddingT, paddingB)); tex.SetView(spine.GetInitView(resolution, padding));
tex.Clear(SFML.Graphics.Color.Transparent); tex.Clear(SFML.Graphics.Color.Transparent);
tex.Draw(spine); tex.Draw(spine);
tex.Display(); tex.Display();
spine.CurrentAnimation = tmp; spine.CurrentAnimation = tmp;
try try
{ {
using (var img = tex.Texture.CopyToImage()) using (var img = new Bitmap(tex.Texture.CopyToBitmap()))
{ {
img.SaveToFile(Path.Combine(outputDir, $"{spine.Name}.png")); img.SetResolution(dpi.Width, dpi.Height);
img.Save(savePath, imageFormat);
} }
success++; success++;
} }
@@ -365,28 +373,19 @@ namespace SpineViewer
private void ConvertFileFormat_Work(object? sender, DoWorkEventArgs e) private void ConvertFileFormat_Work(object? sender, DoWorkEventArgs e)
{ {
var worker = sender as BackgroundWorker; var worker = sender as BackgroundWorker;
var arguments = e.Argument as Dialogs.ConvertFileFormatDialog; var arguments = e.Argument as Dialogs.ConvertFileFormatDialogResult;
var skelPaths = arguments.SkelPaths; var skelPaths = arguments.SkelPaths;
var srcVersion = arguments.SourceVersion; var srcVersion = arguments.SourceVersion;
var tgtVersion = arguments.TargetVersion; var tgtVersion = arguments.TargetVersion;
var jsonSource = arguments.JsonSource;
var jsonTarget = arguments.JsonTarget; var jsonTarget = arguments.JsonTarget;
var newSuffix = jsonTarget ? ".json" : ".skel"; var newSuffix = jsonTarget ? ".json" : ".skel";
if (jsonTarget == jsonSource)
{
if (tgtVersion == srcVersion)
return;
else
newSuffix += $".{tgtVersion.ToString().ToLower()}"; // TODO: 仅转换版本的情况下考虑文件覆盖问题
}
int totalCount = skelPaths.Length; int totalCount = skelPaths.Length;
int success = 0; int success = 0;
int error = 0; int error = 0;
SkeletonConverter srcCvter = SkeletonConverter.New(srcVersion); SkeletonConverter srcCvter = srcVersion != Spine.Version.Auto ? SkeletonConverter.New(srcVersion) : null;
SkeletonConverter tgtCvter = tgtVersion == srcVersion ? srcCvter : SkeletonConverter.New(tgtVersion); SkeletonConverter tgtCvter = SkeletonConverter.New(tgtVersion);
worker.ReportProgress(0, $"已处理 0/{totalCount}"); worker.ReportProgress(0, $"已处理 0/{totalCount}");
for (int i = 0; i < totalCount; i++) for (int i = 0; i < totalCount; i++)
@@ -402,8 +401,15 @@ namespace SpineViewer
try try
{ {
var root = jsonSource ? srcCvter.ReadJson(skelPath) : srcCvter.ReadBinary(skelPath); if (srcVersion == Spine.Version.Auto)
if (tgtVersion != srcVersion) root = srcCvter.ToVersion(root, tgtVersion); {
if (Spine.Spine.GetVersion(skelPath) is Spine.Version detectedSrcVersion)
srcCvter = SkeletonConverter.New(detectedSrcVersion);
else
throw new InvalidDataException($"Auto version detection failed for {skelPath}, try to use a specific version");
}
var root = srcCvter.Read(skelPath);
root = srcCvter.ToVersion(root, tgtVersion);
if (jsonTarget) tgtCvter.WriteJson(root, newPath); else tgtCvter.WriteBinary(root, newPath); if (jsonTarget) tgtCvter.WriteJson(root, newPath); else tgtCvter.WriteBinary(root, newPath);
success++; success++;
} }

View File

@@ -111,6 +111,29 @@ namespace SpineViewer.Spine
root.WriteTo(writer); root.WriteTo(writer);
} }
/// <summary>
/// 读取骨骼文件
/// </summary>
public JsonObject Read(string path)
{
try
{
return ReadBinary(path);
}
catch
{
try
{
return ReadJson(path);
}
catch
{
// 都不行就报错
throw new InvalidDataException($"Unknown skeleton file format {path}");
}
}
}
/// <summary> /// <summary>
/// 转换到目标版本 /// 转换到目标版本
/// </summary> /// </summary>

View File

@@ -8,7 +8,7 @@
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<BaseOutputPath>$(SolutionDir)out</BaseOutputPath> <BaseOutputPath>$(SolutionDir)out</BaseOutputPath>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion> <IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<Version>0.10.7</Version> <Version>0.10.9</Version>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<UseWindowsForms>true</UseWindowsForms> <UseWindowsForms>true</UseWindowsForms>
<ApplicationIcon>appicon.ico</ApplicationIcon> <ApplicationIcon>appicon.ico</ApplicationIcon>