Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8de00cad76 | ||
|
|
e4c58f2f4e | ||
|
|
063dba30b6 | ||
|
|
01fa9287a1 | ||
|
|
008067fccb | ||
|
|
091301e945 | ||
|
|
145f4f3265 | ||
|
|
36d4e8c948 | ||
|
|
63de847a57 | ||
|
|
b3e1b7c902 | ||
|
|
2dbc235631 | ||
|
|
4d68b48367 | ||
|
|
65e63e2b2d | ||
|
|
58071e1de1 | ||
|
|
5009ef479f | ||
|
|
e5e9357649 | ||
|
|
a577474772 | ||
|
|
e960a09153 | ||
|
|
13d50f59c3 | ||
|
|
ed4c8475e9 | ||
|
|
2338bf4e15 | ||
|
|
267aa7ee63 | ||
|
|
3df7dbc769 | ||
|
|
5f12ab7e85 |
2
.github/workflows/dotnet-desktop.yml
vendored
2
.github/workflows/dotnet-desktop.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Build and Release
|
||||
name: Build & Release
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# CHANGELOG
|
||||
|
||||
## v0.10.1
|
||||
|
||||
- <20><><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD>Ԥ<EFBFBD><D4A4>ͼ
|
||||
- <20><><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD>Ԥ<EFBFBD><D4A4>ͼ<EFBFBD><CDBC><EFBFBD><EFBFBD>
|
||||
|
||||
## v0.10.0
|
||||
|
||||
- <20><><EFBFBD><EFBFBD><EFBFBD>˻<EFBFBD><CBBB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD><D0B1><EFBFBD>ѡ<EFBFBD><D1A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɾ<EFBFBD><C9BE><EFBFBD><EFBFBD>Ԥ<EFBFBD><D4A4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʾ<EFBFBD><CABE>Χ<EFBFBD><CEA7>ѡ<EFBFBD><D1A1>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# SpineViewer
|
||||
# [SpineViewer](https://github.com/ww-rm/SpineViewer)
|
||||
|
||||
[](https://github.com/ww-rm/SpineViewer/actions/workflows/dotnet-desktop.yml)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
A simple and user-friendly Spine file viewer and exporter.
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# SpineViewer
|
||||
# [SpineViewer](https://github.com/ww-rm/SpineViewer)
|
||||
|
||||
[](https://github.com/ww-rm/SpineViewer/actions/workflows/dotnet-desktop.yml)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
一个简单好用的 Spine 文件查看&导出程序.
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
## 功能支持
|
||||
|
||||
| 版本 | 查看&导出 | 转换 |
|
||||
| 版本 | 查看&导出 | 格式转换 |
|
||||
| :---: | :---: | :---: |
|
||||
| `2.1.x` | :white_check_mark: | |
|
||||
| `3.1.x` | | |
|
||||
@@ -28,7 +28,7 @@
|
||||
| `3.5.x` | | |
|
||||
| `3.6.x` | :white_check_mark: | |
|
||||
| `3.7.x` | :white_check_mark: | |
|
||||
| `3.8.x` | :white_check_mark: | |
|
||||
| `3.8.x` | :white_check_mark: | :white_check_mark: |
|
||||
| `4.1.x` | :white_check_mark: | |
|
||||
| `4.2.x` | :white_check_mark: | |
|
||||
| `4.3.x` | | |
|
||||
@@ -37,6 +37,7 @@
|
||||
- 支持每个骨骼独立参数设置
|
||||
- 支持动画PNG帧序列导出
|
||||
- 支持缩放旋转等导出画面设置
|
||||
- 支持对独立的骨骼文件进行格式转换
|
||||
- Coming soon...
|
||||
|
||||
## 使用方法
|
||||
|
||||
65
SpineViewer/Controls/SpineListView.Designer.cs
generated
65
SpineViewer/Controls/SpineListView.Designer.cs
generated
@@ -41,6 +41,13 @@
|
||||
toolStripSeparator2 = new ToolStripSeparator();
|
||||
toolStripMenuItem_BatchAdd = new ToolStripMenuItem();
|
||||
toolStripMenuItem_RemoveAll = new ToolStripMenuItem();
|
||||
toolStripSeparator3 = new ToolStripSeparator();
|
||||
toolStripMenuItem_ChangeView = new ToolStripMenuItem();
|
||||
toolStripMenuItem_LargeIconView = new ToolStripMenuItem();
|
||||
toolStripMenuItem_SmallIconView = new ToolStripMenuItem();
|
||||
toolStripMenuItem_DetailsView = new ToolStripMenuItem();
|
||||
imageList_LargeIcon = new ImageList(components);
|
||||
imageList_SmallIcon = new ImageList(components);
|
||||
contextMenuStrip.SuspendLayout();
|
||||
SuspendLayout();
|
||||
//
|
||||
@@ -52,10 +59,12 @@
|
||||
listView.Dock = DockStyle.Fill;
|
||||
listView.FullRowSelect = true;
|
||||
listView.GridLines = true;
|
||||
listView.LargeImageList = imageList_LargeIcon;
|
||||
listView.Location = new Point(0, 0);
|
||||
listView.Name = "listView";
|
||||
listView.ShowItemToolTips = true;
|
||||
listView.Size = new Size(336, 445);
|
||||
listView.SmallImageList = imageList_SmallIcon;
|
||||
listView.TabIndex = 1;
|
||||
listView.UseCompatibleStateImageBehavior = false;
|
||||
listView.View = View.Details;
|
||||
@@ -73,9 +82,9 @@
|
||||
// contextMenuStrip
|
||||
//
|
||||
contextMenuStrip.ImageScalingSize = new Size(24, 24);
|
||||
contextMenuStrip.Items.AddRange(new ToolStripItem[] { toolStripMenuItem_Add, toolStripMenuItem_Insert, toolStripMenuItem_Remove, toolStripSeparator1, toolStripMenuItem_MoveUp, toolStripMenuItem_MoveDown, toolStripSeparator2, toolStripMenuItem_BatchAdd, toolStripMenuItem_RemoveAll });
|
||||
contextMenuStrip.Items.AddRange(new ToolStripItem[] { toolStripMenuItem_Add, toolStripMenuItem_Insert, toolStripMenuItem_Remove, toolStripSeparator1, toolStripMenuItem_MoveUp, toolStripMenuItem_MoveDown, toolStripSeparator2, toolStripMenuItem_BatchAdd, toolStripMenuItem_RemoveAll, toolStripSeparator3, toolStripMenuItem_ChangeView });
|
||||
contextMenuStrip.Name = "contextMenuStrip";
|
||||
contextMenuStrip.Size = new Size(188, 226);
|
||||
contextMenuStrip.Size = new Size(188, 262);
|
||||
contextMenuStrip.Opening += contextMenuStrip_Opening;
|
||||
//
|
||||
// toolStripMenuItem_Add
|
||||
@@ -140,6 +149,51 @@
|
||||
toolStripMenuItem_RemoveAll.Text = "移除全部(&X)";
|
||||
toolStripMenuItem_RemoveAll.Click += toolStripMenuItem_RemoveAll_Click;
|
||||
//
|
||||
// toolStripSeparator3
|
||||
//
|
||||
toolStripSeparator3.Name = "toolStripSeparator3";
|
||||
toolStripSeparator3.Size = new Size(184, 6);
|
||||
//
|
||||
// toolStripMenuItem_ChangeView
|
||||
//
|
||||
toolStripMenuItem_ChangeView.DropDownItems.AddRange(new ToolStripItem[] { toolStripMenuItem_LargeIconView, toolStripMenuItem_SmallIconView, toolStripMenuItem_DetailsView });
|
||||
toolStripMenuItem_ChangeView.Name = "toolStripMenuItem_ChangeView";
|
||||
toolStripMenuItem_ChangeView.Size = new Size(187, 30);
|
||||
toolStripMenuItem_ChangeView.Text = "切换视图";
|
||||
//
|
||||
// toolStripMenuItem_LargeIconView
|
||||
//
|
||||
toolStripMenuItem_LargeIconView.Name = "toolStripMenuItem_LargeIconView";
|
||||
toolStripMenuItem_LargeIconView.Size = new Size(164, 34);
|
||||
toolStripMenuItem_LargeIconView.Text = "大图标";
|
||||
toolStripMenuItem_LargeIconView.Click += toolStripMenuItem_LargeIconView_Click;
|
||||
//
|
||||
// toolStripMenuItem_SmallIconView
|
||||
//
|
||||
toolStripMenuItem_SmallIconView.Name = "toolStripMenuItem_SmallIconView";
|
||||
toolStripMenuItem_SmallIconView.Size = new Size(164, 34);
|
||||
toolStripMenuItem_SmallIconView.Text = "小图标";
|
||||
toolStripMenuItem_SmallIconView.Click += toolStripMenuItem_SmallIconView_Click;
|
||||
//
|
||||
// toolStripMenuItem_DetailsView
|
||||
//
|
||||
toolStripMenuItem_DetailsView.Name = "toolStripMenuItem_DetailsView";
|
||||
toolStripMenuItem_DetailsView.Size = new Size(164, 34);
|
||||
toolStripMenuItem_DetailsView.Text = "列表";
|
||||
toolStripMenuItem_DetailsView.Click += toolStripMenuItem_DetailsView_Click;
|
||||
//
|
||||
// imageList_LargeIcon
|
||||
//
|
||||
imageList_LargeIcon.ColorDepth = ColorDepth.Depth32Bit;
|
||||
imageList_LargeIcon.ImageSize = new Size(96, 96);
|
||||
imageList_LargeIcon.TransparentColor = Color.Transparent;
|
||||
//
|
||||
// imageList_SmallIcon
|
||||
//
|
||||
imageList_SmallIcon.ColorDepth = ColorDepth.Depth32Bit;
|
||||
imageList_SmallIcon.ImageSize = new Size(48, 48);
|
||||
imageList_SmallIcon.TransparentColor = Color.Transparent;
|
||||
//
|
||||
// SpineListView
|
||||
//
|
||||
AutoScaleDimensions = new SizeF(11F, 24F);
|
||||
@@ -164,5 +218,12 @@
|
||||
private ToolStripMenuItem toolStripMenuItem_MoveDown;
|
||||
private ToolStripSeparator toolStripSeparator2;
|
||||
private ColumnHeader columnHeader_Name;
|
||||
private ImageList imageList_SmallIcon;
|
||||
private ImageList imageList_LargeIcon;
|
||||
private ToolStripSeparator toolStripSeparator3;
|
||||
private ToolStripMenuItem toolStripMenuItem_ChangeView;
|
||||
private ToolStripMenuItem toolStripMenuItem_LargeIconView;
|
||||
private ToolStripMenuItem toolStripMenuItem_SmallIconView;
|
||||
private ToolStripMenuItem toolStripMenuItem_DetailsView;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,41 +40,6 @@ namespace SpineViewer.Controls
|
||||
/// </summary>
|
||||
public ListView.SelectedIndexCollection SelectedIndices { get => listView.SelectedIndices; }
|
||||
|
||||
/// <summary>
|
||||
/// 弹出添加对话框在指定位置之前插入一项
|
||||
/// </summary>
|
||||
private void Insert(int index = -1)
|
||||
{
|
||||
var dialog = new Dialogs.OpenSpineDialog();
|
||||
if (dialog.ShowDialog() != DialogResult.OK)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
var spine = Spine.Spine.New(dialog.Version, dialog.SkelPath, dialog.AtlasPath);
|
||||
|
||||
// 如果索引无效则在末尾添加
|
||||
if (index < 0 || index > listView.Items.Count)
|
||||
index = listView.Items.Count;
|
||||
|
||||
// 锁定外部的读操作
|
||||
lock (Spines) { spines.Insert(index, spine); }
|
||||
listView.Items.Insert(index, new ListViewItem(spine.Name) { ToolTipText = spine.SkelPath });
|
||||
|
||||
// 选中新增项
|
||||
listView.SelectedIndices.Clear();
|
||||
listView.SelectedIndices.Add(index);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Program.Logger.Error(ex.ToString());
|
||||
Program.Logger.Error("Failed to load {} {}", dialog.SkelPath, dialog.AtlasPath);
|
||||
MessageBox.Show(ex.ToString(), "骨骼加载失败", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
|
||||
Program.Logger.Info($"Current memory usage: {Program.Process.WorkingSet64 / 1024.0 / 1024.0:F2} MB");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 弹出添加对话框
|
||||
/// </summary>
|
||||
@@ -98,55 +63,25 @@ namespace SpineViewer.Controls
|
||||
progressDialog.ShowDialog();
|
||||
}
|
||||
|
||||
private void BatchAdd_Work(object? sender, DoWorkEventArgs e)
|
||||
public void ExportPreviews()
|
||||
{
|
||||
var worker = sender as BackgroundWorker;
|
||||
var arguments = e.Argument as Dialogs.BatchOpenSpineDialog;
|
||||
var skelPaths = arguments.SkelPaths;
|
||||
var version = arguments.Version;
|
||||
|
||||
int totalCount = skelPaths.Length;
|
||||
int success = 0;
|
||||
int error = 0;
|
||||
|
||||
worker.ReportProgress(0, $"已处理 0/{totalCount}");
|
||||
for (int i = 0; i < totalCount; i++)
|
||||
lock (Spines)
|
||||
{
|
||||
if (worker.CancellationPending)
|
||||
if (spines.Count <= 0)
|
||||
{
|
||||
e.Cancel = true;
|
||||
break;
|
||||
MessageBox.Show("请至少打开一个骨骼文件", "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
var skelPath = skelPaths[i];
|
||||
|
||||
try
|
||||
{
|
||||
var spine = Spine.Spine.New(version, skelPath);
|
||||
lock (Spines) { spines.Add(spine); }
|
||||
listView.Invoke(() => listView.Items.Add(new ListViewItem(spine.Name) { ToolTipText = spine.SkelPath }));
|
||||
success++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Program.Logger.Error(ex.ToString());
|
||||
Program.Logger.Error("Failed to load {}", skelPath);
|
||||
error++;
|
||||
}
|
||||
|
||||
worker.ReportProgress((int)((i + 1) * 100.0) / totalCount, $"已处理 {i + 1}/{totalCount}");
|
||||
}
|
||||
|
||||
if (error > 0)
|
||||
{
|
||||
Program.Logger.Warn("Batch load {} successfully, {} failed", success, error);
|
||||
}
|
||||
else
|
||||
{
|
||||
Program.Logger.Info("{} skel loaded successfully", success);
|
||||
}
|
||||
var saveDialog = new Dialogs.ExportPreviewDialog();
|
||||
if (saveDialog.ShowDialog() != DialogResult.OK)
|
||||
return;
|
||||
|
||||
Program.Logger.Info($"Current memory usage: {Program.Process.WorkingSet64 / 1024.0 / 1024.0:F2} MB");
|
||||
var progressDialog = new Dialogs.ProgressDialog();
|
||||
progressDialog.DoWork += ExportPreview_Work;
|
||||
progressDialog.RunWorkerAsync(saveDialog);
|
||||
progressDialog.ShowDialog();
|
||||
}
|
||||
|
||||
private void listView_SelectedIndexChanged(object sender, EventArgs e)
|
||||
@@ -255,6 +190,11 @@ namespace SpineViewer.Controls
|
||||
toolStripMenuItem_MoveUp.Enabled = selectedCount == 1 && listView.SelectedIndices[0] != 0;
|
||||
toolStripMenuItem_MoveDown.Enabled = selectedCount == 1 && listView.SelectedIndices[0] != itemsCount - 1;
|
||||
toolStripMenuItem_RemoveAll.Enabled = itemsCount > 0;
|
||||
|
||||
// 视图选项
|
||||
toolStripMenuItem_LargeIconView.Checked = listView.View == View.LargeIcon;
|
||||
toolStripMenuItem_SmallIconView.Checked = listView.View == View.SmallIcon;
|
||||
toolStripMenuItem_DetailsView.Checked = listView.View == View.Details;
|
||||
}
|
||||
|
||||
private void toolStripMenuItem_Add_Click(object sender, EventArgs e)
|
||||
@@ -288,8 +228,11 @@ namespace SpineViewer.Controls
|
||||
{
|
||||
lock (Spines)
|
||||
{
|
||||
spines[i].Dispose();
|
||||
var spine = spines[i];
|
||||
spines.RemoveAt(i);
|
||||
listView.SmallImageList.Images.RemoveByKey(spine.ID);
|
||||
listView.LargeImageList.Images.RemoveByKey(spine.ID);
|
||||
spine.Dispose();
|
||||
}
|
||||
listView.Items.RemoveAt(i);
|
||||
}
|
||||
@@ -338,10 +281,177 @@ namespace SpineViewer.Controls
|
||||
foreach (var spine in spines)
|
||||
spine.Dispose();
|
||||
spines.Clear();
|
||||
listView.SmallImageList.Images.Clear();
|
||||
listView.LargeImageList.Images.Clear();
|
||||
}
|
||||
listView.Items.Clear();
|
||||
if (PropertyGrid is not null)
|
||||
PropertyGrid.SelectedObject = null;
|
||||
}
|
||||
|
||||
private void toolStripMenuItem_LargeIconView_Click(object sender, EventArgs e)
|
||||
{
|
||||
listView.View = View.LargeIcon;
|
||||
}
|
||||
|
||||
private void toolStripMenuItem_SmallIconView_Click(object sender, EventArgs e)
|
||||
{
|
||||
listView.View = View.SmallIcon;
|
||||
}
|
||||
|
||||
private void toolStripMenuItem_DetailsView_Click(object sender, EventArgs e)
|
||||
{
|
||||
listView.View = View.Details;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 弹出添加对话框在指定位置之前插入一项
|
||||
/// </summary>
|
||||
private void Insert(int index = -1)
|
||||
{
|
||||
var dialog = new Dialogs.OpenSpineDialog();
|
||||
if (dialog.ShowDialog() != DialogResult.OK)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
var spine = Spine.Spine.New(dialog.Version, dialog.SkelPath, dialog.AtlasPath);
|
||||
|
||||
// 如果索引无效则在末尾添加
|
||||
if (index < 0 || index > listView.Items.Count)
|
||||
index = listView.Items.Count;
|
||||
|
||||
// 锁定外部的读操作
|
||||
lock (Spines)
|
||||
{
|
||||
spines.Insert(index, spine);
|
||||
listView.SmallImageList.Images.Add(spine.ID, spine.Preview);
|
||||
listView.LargeImageList.Images.Add(spine.ID, spine.Preview);
|
||||
}
|
||||
listView.Items.Insert(index, new ListViewItem(spine.Name, spine.ID) { ToolTipText = spine.SkelPath });
|
||||
|
||||
// 选中新增项
|
||||
listView.SelectedIndices.Clear();
|
||||
listView.SelectedIndices.Add(index);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Program.Logger.Error(ex.ToString());
|
||||
Program.Logger.Error("Failed to load {} {}", dialog.SkelPath, dialog.AtlasPath);
|
||||
MessageBox.Show(ex.ToString(), "骨骼加载失败", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
|
||||
Program.Logger.Info($"Current memory usage: {Program.Process.WorkingSet64 / 1024.0 / 1024.0:F2} MB");
|
||||
}
|
||||
|
||||
private void BatchAdd_Work(object? sender, DoWorkEventArgs e)
|
||||
{
|
||||
var worker = sender as BackgroundWorker;
|
||||
var arguments = e.Argument as Dialogs.BatchOpenSpineDialog;
|
||||
var skelPaths = arguments.SkelPaths;
|
||||
var version = arguments.Version;
|
||||
|
||||
int totalCount = skelPaths.Length;
|
||||
int success = 0;
|
||||
int error = 0;
|
||||
|
||||
worker.ReportProgress(0, $"已处理 0/{totalCount}");
|
||||
for (int i = 0; i < totalCount; i++)
|
||||
{
|
||||
if (worker.CancellationPending)
|
||||
{
|
||||
e.Cancel = true;
|
||||
break;
|
||||
}
|
||||
|
||||
var skelPath = skelPaths[i];
|
||||
|
||||
try
|
||||
{
|
||||
var spine = Spine.Spine.New(version, skelPath);
|
||||
var preview = spine.Preview;
|
||||
lock (Spines) { spines.Add(spine); }
|
||||
listView.Invoke(() =>
|
||||
{
|
||||
listView.SmallImageList.Images.Add(spine.ID, preview);
|
||||
listView.LargeImageList.Images.Add(spine.ID, preview);
|
||||
listView.Items.Add(new ListViewItem(spine.Name, spine.ID) { ToolTipText = spine.SkelPath });
|
||||
});
|
||||
success++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Program.Logger.Error(ex.ToString());
|
||||
Program.Logger.Error("Failed to load {}", skelPath);
|
||||
error++;
|
||||
}
|
||||
|
||||
worker.ReportProgress((int)((i + 1) * 100.0) / totalCount, $"已处理 {i + 1}/{totalCount}");
|
||||
}
|
||||
|
||||
if (error > 0)
|
||||
{
|
||||
Program.Logger.Warn("Batch load {} successfully, {} failed", success, error);
|
||||
}
|
||||
else
|
||||
{
|
||||
Program.Logger.Info("{} skel loaded successfully", success);
|
||||
}
|
||||
|
||||
Program.Logger.Info($"Current memory usage: {Program.Process.WorkingSet64 / 1024.0 / 1024.0:F2} MB");
|
||||
}
|
||||
|
||||
private void ExportPreview_Work(object? sender, DoWorkEventArgs e)
|
||||
{
|
||||
var worker = sender as BackgroundWorker;
|
||||
var arguments = e.Argument as Dialogs.ExportPreviewDialog;
|
||||
var outputDir = arguments.OutputDir;
|
||||
var width = arguments.PreviewWidth;
|
||||
var height = arguments.PreviewHeight;
|
||||
|
||||
int success = 0;
|
||||
int error = 0;
|
||||
lock (Spines)
|
||||
{
|
||||
int totalCount = spines.Count;
|
||||
worker.ReportProgress(0, $"已处理 0/{totalCount}");
|
||||
for (int i = 0; i < totalCount; i++)
|
||||
{
|
||||
if (worker.CancellationPending)
|
||||
{
|
||||
e.Cancel = true;
|
||||
break;
|
||||
}
|
||||
|
||||
var spine = spines[i];
|
||||
try
|
||||
{
|
||||
var preview = spine.GetPreview(width, height);
|
||||
var savePath = Path.Combine(outputDir, $"{spine.Name}.png");
|
||||
preview.SaveToFile(savePath);
|
||||
success++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Program.Logger.Error(ex.ToString());
|
||||
Program.Logger.Error("Failed to save preview {}", spine.SkelPath);
|
||||
error++;
|
||||
}
|
||||
|
||||
worker.ReportProgress((int)((i + 1) * 100.0) / totalCount, $"已处理 {i + 1}/{totalCount}");
|
||||
}
|
||||
}
|
||||
|
||||
if (error > 0)
|
||||
{
|
||||
Program.Logger.Warn("Preview save {} successfully, {} failed", success, error);
|
||||
}
|
||||
else
|
||||
{
|
||||
Program.Logger.Info("{} preview saved successfully", success);
|
||||
}
|
||||
|
||||
Program.Logger.Info($"Current memory usage: {Program.Process.WorkingSet64 / 1024.0 / 1024.0:F2} MB");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,4 +120,10 @@
|
||||
<metadata name="contextMenuStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<metadata name="imageList_LargeIcon.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>552, 29</value>
|
||||
</metadata>
|
||||
<metadata name="imageList_SmallIcon.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>267, 34</value>
|
||||
</metadata>
|
||||
</root>
|
||||
@@ -37,7 +37,7 @@
|
||||
label1 = new Label();
|
||||
label4 = new Label();
|
||||
label3 = new Label();
|
||||
comboBox_Version = new ComboBox();
|
||||
comboBox_SourceVersion = new ComboBox();
|
||||
tableLayoutPanel2 = new TableLayoutPanel();
|
||||
button_Ok = new Button();
|
||||
button_Cancel = new Button();
|
||||
@@ -75,7 +75,7 @@
|
||||
tableLayoutPanel1.Controls.Add(label1, 0, 4);
|
||||
tableLayoutPanel1.Controls.Add(label4, 0, 0);
|
||||
tableLayoutPanel1.Controls.Add(label3, 0, 3);
|
||||
tableLayoutPanel1.Controls.Add(comboBox_Version, 1, 3);
|
||||
tableLayoutPanel1.Controls.Add(comboBox_SourceVersion, 1, 3);
|
||||
tableLayoutPanel1.Controls.Add(tableLayoutPanel2, 0, 6);
|
||||
tableLayoutPanel1.Controls.Add(listBox_FilePath, 0, 2);
|
||||
tableLayoutPanel1.Controls.Add(button_SelectSkel, 0, 1);
|
||||
@@ -166,14 +166,14 @@
|
||||
//
|
||||
// comboBox_Version
|
||||
//
|
||||
comboBox_Version.Anchor = AnchorStyles.Left;
|
||||
comboBox_Version.DropDownStyle = ComboBoxStyle.DropDownList;
|
||||
comboBox_Version.FormattingEnabled = true;
|
||||
comboBox_Version.Location = new Point(146, 303);
|
||||
comboBox_Version.Name = "comboBox_Version";
|
||||
comboBox_Version.Size = new Size(182, 32);
|
||||
comboBox_Version.Sorted = true;
|
||||
comboBox_Version.TabIndex = 13;
|
||||
comboBox_SourceVersion.Anchor = AnchorStyles.Left;
|
||||
comboBox_SourceVersion.DropDownStyle = ComboBoxStyle.DropDownList;
|
||||
comboBox_SourceVersion.FormattingEnabled = true;
|
||||
comboBox_SourceVersion.Location = new Point(146, 303);
|
||||
comboBox_SourceVersion.Name = "comboBox_Version";
|
||||
comboBox_SourceVersion.Size = new Size(182, 32);
|
||||
comboBox_SourceVersion.Sorted = true;
|
||||
comboBox_SourceVersion.TabIndex = 13;
|
||||
//
|
||||
// tableLayoutPanel2
|
||||
//
|
||||
@@ -320,6 +320,7 @@
|
||||
ShowInTaskbar = false;
|
||||
StartPosition = FormStartPosition.CenterScreen;
|
||||
Text = "骨骼文件格式转换";
|
||||
Load += ConvertFileFormatDialog_Load;
|
||||
panel.ResumeLayout(false);
|
||||
tableLayoutPanel1.ResumeLayout(false);
|
||||
tableLayoutPanel1.PerformLayout();
|
||||
@@ -337,7 +338,7 @@
|
||||
private TableLayoutPanel tableLayoutPanel1;
|
||||
private Label label4;
|
||||
private Label label3;
|
||||
private ComboBox comboBox_Version;
|
||||
private ComboBox comboBox_SourceVersion;
|
||||
private TableLayoutPanel tableLayoutPanel2;
|
||||
private Button button_Ok;
|
||||
private Button button_Cancel;
|
||||
|
||||
@@ -14,16 +14,27 @@ namespace SpineViewer.Dialogs
|
||||
public partial class ConvertFileFormatDialog : Form
|
||||
{
|
||||
public string[] SkelPaths { get; private set; }
|
||||
public Spine.Version Version { get; private set; }
|
||||
public bool ConvertToJson { get; private set; }
|
||||
public Spine.Version SourceVersion { get; private set; }
|
||||
public Spine.Version TargetVersion { get; private set; }
|
||||
public bool JsonSource { get; private set; }
|
||||
public bool JsonTarget { get; private set; }
|
||||
|
||||
public ConvertFileFormatDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
comboBox_Version.DataSource = VersionHelper.Versions.ToList();
|
||||
comboBox_Version.DisplayMember = "Value";
|
||||
comboBox_Version.ValueMember = "Key";
|
||||
comboBox_Version.SelectedValue = Spine.Version.V38;
|
||||
comboBox_SourceVersion.DataSource = VersionHelper.Versions.ToList();
|
||||
comboBox_SourceVersion.DisplayMember = "Value";
|
||||
comboBox_SourceVersion.ValueMember = "Key";
|
||||
comboBox_SourceVersion.SelectedValue = Spine.Version.V38;
|
||||
//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)
|
||||
{
|
||||
button_SelectSkel_Click(sender, e);
|
||||
}
|
||||
|
||||
private void button_SelectSkel_Click(object sender, EventArgs e)
|
||||
@@ -39,7 +50,10 @@ namespace SpineViewer.Dialogs
|
||||
|
||||
private void button_Ok_Click(object sender, EventArgs e)
|
||||
{
|
||||
var version = (Spine.Version)comboBox_Version.SelectedValue;
|
||||
var sourceVersion = (Spine.Version)comboBox_SourceVersion.SelectedValue;
|
||||
var targetVersion = (Spine.Version)comboBox_SourceVersion.SelectedValue; // TODO: 增加目标版本
|
||||
var jsonSource = radioButton_JsonSource.Checked;
|
||||
var jsonTarget = radioButton_JsonTarget.Checked;
|
||||
|
||||
if (listBox_FilePath.Items.Count <= 0)
|
||||
{
|
||||
@@ -56,16 +70,29 @@ namespace SpineViewer.Dialogs
|
||||
}
|
||||
}
|
||||
|
||||
if (!Spine.Spine.ImplementedVersions.Contains(version) ||
|
||||
!SkeletonConverter.ImplementedVersions.Contains(version))
|
||||
if (!SkeletonConverter.ImplementedVersions.Contains(sourceVersion))
|
||||
{
|
||||
MessageBox.Show($"{version.String()} 版本尚未实现(咕咕咕~)", "错误信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
MessageBox.Show($"{sourceVersion.String()} 版本尚未实现(咕咕咕~)", "错误信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SkeletonConverter.ImplementedVersions.Contains(targetVersion))
|
||||
{
|
||||
MessageBox.Show($"{targetVersion.String()} 版本尚未实现(咕咕咕~)", "错误信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
if (jsonSource == jsonTarget && sourceVersion == targetVersion)
|
||||
{
|
||||
MessageBox.Show($"不需要转换相同的格式和版本", "错误信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
SkelPaths = listBox_FilePath.Items.Cast<string>().ToArray();
|
||||
Version = version;
|
||||
ConvertToJson = radioButton_BinarySource.Checked;
|
||||
SourceVersion = sourceVersion;
|
||||
TargetVersion = targetVersion;
|
||||
JsonSource = jsonSource;
|
||||
JsonTarget = jsonTarget;
|
||||
|
||||
DialogResult = DialogResult.OK;
|
||||
}
|
||||
|
||||
1
SpineViewer/Dialogs/ExportPngDialog.Designer.cs
generated
1
SpineViewer/Dialogs/ExportPngDialog.Designer.cs
generated
@@ -239,6 +239,7 @@
|
||||
ShowInTaskbar = false;
|
||||
StartPosition = FormStartPosition.CenterScreen;
|
||||
Text = "导出PNG序列";
|
||||
Load += ExportPngDialog_Load;
|
||||
panel1.ResumeLayout(false);
|
||||
panel1.PerformLayout();
|
||||
tableLayoutPanel1.ResumeLayout(false);
|
||||
|
||||
@@ -21,6 +21,11 @@ namespace SpineViewer.Dialogs
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void ExportPngDialog_Load(object sender, EventArgs e)
|
||||
{
|
||||
button_SelectOutputDir_Click(sender, e);
|
||||
}
|
||||
|
||||
private void button_SelectOutputDir_Click(object sender, EventArgs e)
|
||||
{
|
||||
folderBrowserDialog.InitialDirectory = textBox_OutputDir.Text;
|
||||
|
||||
270
SpineViewer/Dialogs/ExportPreviewDialog.Designer.cs
generated
Normal file
270
SpineViewer/Dialogs/ExportPreviewDialog.Designer.cs
generated
Normal file
@@ -0,0 +1,270 @@
|
||||
namespace SpineViewer.Dialogs
|
||||
{
|
||||
partial class ExportPreviewDialog
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ExportPreviewDialog));
|
||||
panel1 = new Panel();
|
||||
tableLayoutPanel1 = new TableLayoutPanel();
|
||||
label4 = new Label();
|
||||
label1 = new Label();
|
||||
label2 = new Label();
|
||||
label3 = new Label();
|
||||
textBox_OutputDir = new TextBox();
|
||||
button_SelectOutputDir = new Button();
|
||||
tableLayoutPanel2 = new TableLayoutPanel();
|
||||
button_Ok = new Button();
|
||||
button_Cancel = new Button();
|
||||
numericUpDown_Width = new NumericUpDown();
|
||||
numericUpDown_Height = new NumericUpDown();
|
||||
folderBrowserDialog = new FolderBrowserDialog();
|
||||
panel1.SuspendLayout();
|
||||
tableLayoutPanel1.SuspendLayout();
|
||||
tableLayoutPanel2.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)numericUpDown_Width).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)numericUpDown_Height).BeginInit();
|
||||
SuspendLayout();
|
||||
//
|
||||
// panel1
|
||||
//
|
||||
panel1.Controls.Add(tableLayoutPanel1);
|
||||
panel1.Dock = DockStyle.Fill;
|
||||
panel1.Location = new Point(0, 0);
|
||||
panel1.Name = "panel1";
|
||||
panel1.Padding = new Padding(50, 15, 50, 10);
|
||||
panel1.Size = new Size(919, 276);
|
||||
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(label4, 0, 0);
|
||||
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(button_SelectOutputDir, 3, 1);
|
||||
tableLayoutPanel1.Controls.Add(tableLayoutPanel2, 0, 4);
|
||||
tableLayoutPanel1.Controls.Add(numericUpDown_Width, 1, 2);
|
||||
tableLayoutPanel1.Controls.Add(numericUpDown_Height, 1, 3);
|
||||
tableLayoutPanel1.Dock = DockStyle.Fill;
|
||||
tableLayoutPanel1.Location = new Point(50, 15);
|
||||
tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||
tableLayoutPanel1.RowCount = 5;
|
||||
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(819, 251);
|
||||
tableLayoutPanel1.TabIndex = 0;
|
||||
//
|
||||
// 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(789, 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 = "输出文件夹:";
|
||||
//
|
||||
// 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
|
||||
//
|
||||
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(664, 30);
|
||||
textBox_OutputDir.TabIndex = 3;
|
||||
//
|
||||
// button_SelectOutputDir
|
||||
//
|
||||
button_SelectOutputDir.AutoSize = true;
|
||||
button_SelectOutputDir.AutoSizeMode = AutoSizeMode.GrowAndShrink;
|
||||
button_SelectOutputDir.Location = new Point(783, 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;
|
||||
//
|
||||
// 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, 208);
|
||||
tableLayoutPanel2.Name = "tableLayoutPanel2";
|
||||
tableLayoutPanel2.RowCount = 1;
|
||||
tableLayoutPanel2.RowStyles.Add(new RowStyle());
|
||||
tableLayoutPanel2.Size = new Size(813, 40);
|
||||
tableLayoutPanel2.TabIndex = 10;
|
||||
//
|
||||
// button_Ok
|
||||
//
|
||||
button_Ok.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
|
||||
button_Ok.Location = new Point(264, 3);
|
||||
button_Ok.Margin = new Padding(3, 3, 30, 3);
|
||||
button_Ok.Name = "button_Ok";
|
||||
button_Ok.Size = new Size(112, 34);
|
||||
button_Ok.TabIndex = 7;
|
||||
button_Ok.Text = "确认";
|
||||
button_Ok.UseVisualStyleBackColor = true;
|
||||
button_Ok.Click += button_Ok_Click;
|
||||
//
|
||||
// button_Cancel
|
||||
//
|
||||
button_Cancel.Anchor = AnchorStyles.Bottom | AnchorStyles.Left;
|
||||
button_Cancel.Location = new Point(436, 3);
|
||||
button_Cancel.Margin = new Padding(30, 3, 3, 3);
|
||||
button_Cancel.Name = "button_Cancel";
|
||||
button_Cancel.Size = new Size(112, 34);
|
||||
button_Cancel.TabIndex = 8;
|
||||
button_Cancel.Text = "取消";
|
||||
button_Cancel.UseVisualStyleBackColor = true;
|
||||
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.AddToRecent = false;
|
||||
//
|
||||
// ExportPreviewDialog
|
||||
//
|
||||
AcceptButton = button_Ok;
|
||||
AutoScaleDimensions = new SizeF(11F, 24F);
|
||||
AutoScaleMode = AutoScaleMode.Font;
|
||||
CancelButton = button_Cancel;
|
||||
ClientSize = new Size(919, 276);
|
||||
Controls.Add(panel1);
|
||||
FormBorderStyle = FormBorderStyle.FixedDialog;
|
||||
Icon = (Icon)resources.GetObject("$this.Icon");
|
||||
MaximizeBox = false;
|
||||
MinimizeBox = false;
|
||||
Name = "ExportPreviewDialog";
|
||||
ShowInTaskbar = false;
|
||||
StartPosition = FormStartPosition.CenterScreen;
|
||||
Text = "导出预览图";
|
||||
Load += ExportPreviewDialog_Load;
|
||||
panel1.ResumeLayout(false);
|
||||
panel1.PerformLayout();
|
||||
tableLayoutPanel1.ResumeLayout(false);
|
||||
tableLayoutPanel1.PerformLayout();
|
||||
tableLayoutPanel2.ResumeLayout(false);
|
||||
((System.ComponentModel.ISupportInitialize)numericUpDown_Width).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)numericUpDown_Height).EndInit();
|
||||
ResumeLayout(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private Panel panel1;
|
||||
private TableLayoutPanel tableLayoutPanel1;
|
||||
private Label label4;
|
||||
private Label label1;
|
||||
private Label label2;
|
||||
private Label label3;
|
||||
private TextBox textBox_OutputDir;
|
||||
private Button button_SelectOutputDir;
|
||||
private TableLayoutPanel tableLayoutPanel2;
|
||||
private Button button_Ok;
|
||||
private Button button_Cancel;
|
||||
private NumericUpDown numericUpDown_Width;
|
||||
private NumericUpDown numericUpDown_Height;
|
||||
private FolderBrowserDialog folderBrowserDialog;
|
||||
}
|
||||
}
|
||||
80
SpineViewer/Dialogs/ExportPreviewDialog.cs
Normal file
80
SpineViewer/Dialogs/ExportPreviewDialog.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace SpineViewer.Dialogs
|
||||
{
|
||||
public partial class ExportPreviewDialog: Form
|
||||
{
|
||||
public string OutputDir { get; private set; }
|
||||
public uint PreviewWidth { get; private set; }
|
||||
public uint PreviewHeight { get; private set; }
|
||||
|
||||
public ExportPreviewDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void ExportPreviewDialog_Load(object sender, EventArgs e)
|
||||
{
|
||||
button_SelectOutputDir_Click(sender, e);
|
||||
}
|
||||
|
||||
private void button_SelectOutputDir_Click(object sender, EventArgs e)
|
||||
{
|
||||
folderBrowserDialog.InitialDirectory = textBox_OutputDir.Text;
|
||||
if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
textBox_OutputDir.Text = Path.GetFullPath(folderBrowserDialog.SelectedPath);
|
||||
}
|
||||
}
|
||||
|
||||
private void button_Ok_Click(object sender, EventArgs e)
|
||||
{
|
||||
var outputDir = textBox_OutputDir.Text;
|
||||
if (File.Exists(outputDir))
|
||||
{
|
||||
MessageBox.Show("输出文件夹无效", "错误信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Directory.Exists(outputDir))
|
||||
{
|
||||
if (MessageBox.Show($"文件夹 {outputDir} 不存在,是否创建?", "操作确认", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.OK)
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(outputDir);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Program.Logger.Error(ex.ToString());
|
||||
MessageBox.Show(ex.ToString(), "文件夹创建失败", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
OutputDir = Path.GetFullPath(outputDir);
|
||||
PreviewWidth = (uint)numericUpDown_Width.Value;
|
||||
PreviewHeight = (uint)numericUpDown_Height.Value;
|
||||
|
||||
DialogResult = DialogResult.OK;
|
||||
}
|
||||
|
||||
private void button_Cancel_Click(object sender, EventArgs e)
|
||||
{
|
||||
DialogResult = DialogResult.Cancel;
|
||||
}
|
||||
}
|
||||
}
|
||||
3270
SpineViewer/Dialogs/ExportPreviewDialog.resx
Normal file
3270
SpineViewer/Dialogs/ExportPreviewDialog.resx
Normal file
File diff suppressed because it is too large
Load Diff
30
SpineViewer/MainForm.Designer.cs
generated
30
SpineViewer/MainForm.Designer.cs
generated
@@ -36,6 +36,7 @@
|
||||
toolStripMenuItem_BatchOpen = new ToolStripMenuItem();
|
||||
toolStripSeparator1 = new ToolStripSeparator();
|
||||
toolStripMenuItem_Export = new ToolStripMenuItem();
|
||||
toolStripMenuItem_ExportPreview = new ToolStripMenuItem();
|
||||
toolStripSeparator2 = new ToolStripSeparator();
|
||||
toolStripMenuItem_Exit = new ToolStripMenuItem();
|
||||
toolStripMenuItem_Function = new ToolStripMenuItem();
|
||||
@@ -100,7 +101,7 @@
|
||||
//
|
||||
// toolStripMenuItem_File
|
||||
//
|
||||
toolStripMenuItem_File.DropDownItems.AddRange(new ToolStripItem[] { toolStripMenuItem_Open, toolStripMenuItem_BatchOpen, toolStripSeparator1, toolStripMenuItem_Export, toolStripSeparator2, toolStripMenuItem_Exit });
|
||||
toolStripMenuItem_File.DropDownItems.AddRange(new ToolStripItem[] { toolStripMenuItem_Open, toolStripMenuItem_BatchOpen, toolStripSeparator1, toolStripMenuItem_Export, toolStripMenuItem_ExportPreview, toolStripSeparator2, toolStripMenuItem_Exit });
|
||||
toolStripMenuItem_File.Name = "toolStripMenuItem_File";
|
||||
toolStripMenuItem_File.Size = new Size(84, 28);
|
||||
toolStripMenuItem_File.Text = "文件(&F)";
|
||||
@@ -133,6 +134,13 @@
|
||||
toolStripMenuItem_Export.Text = "导出(&E)...";
|
||||
toolStripMenuItem_Export.Click += toolStripMenuItem_Export_Click;
|
||||
//
|
||||
// toolStripMenuItem_ExportPreview
|
||||
//
|
||||
toolStripMenuItem_ExportPreview.Name = "toolStripMenuItem_ExportPreview";
|
||||
toolStripMenuItem_ExportPreview.Size = new Size(254, 34);
|
||||
toolStripMenuItem_ExportPreview.Text = "导出预览图(&P)...";
|
||||
toolStripMenuItem_ExportPreview.Click += toolStripMenuItem_ExportPreview_Click;
|
||||
//
|
||||
// toolStripSeparator2
|
||||
//
|
||||
toolStripSeparator2.Name = "toolStripSeparator2";
|
||||
@@ -170,7 +178,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;
|
||||
//
|
||||
@@ -186,6 +194,7 @@
|
||||
toolStripMenuItem_ManageResource.Name = "toolStripMenuItem_ManageResource";
|
||||
toolStripMenuItem_ManageResource.Size = new Size(260, 34);
|
||||
toolStripMenuItem_ManageResource.Text = "管理下载资源(&M)...";
|
||||
toolStripMenuItem_ManageResource.Click += toolStripMenuItem_ManageResource_Click;
|
||||
//
|
||||
// toolStripMenuItem_Help
|
||||
//
|
||||
@@ -292,7 +301,7 @@
|
||||
splitContainer_Information.Panel2.Controls.Add(splitContainer_Config);
|
||||
splitContainer_Information.Panel2.Cursor = Cursors.Default;
|
||||
splitContainer_Information.Size = new Size(744, 848);
|
||||
splitContainer_Information.SplitterDistance = 327;
|
||||
splitContainer_Information.SplitterDistance = 398;
|
||||
splitContainer_Information.TabIndex = 1;
|
||||
splitContainer_Information.TabStop = false;
|
||||
splitContainer_Information.SplitterMoved += splitContainer_SplitterMoved;
|
||||
@@ -304,7 +313,7 @@
|
||||
groupBox_SkelList.Dock = DockStyle.Fill;
|
||||
groupBox_SkelList.Location = new Point(0, 0);
|
||||
groupBox_SkelList.Name = "groupBox_SkelList";
|
||||
groupBox_SkelList.Size = new Size(327, 848);
|
||||
groupBox_SkelList.Size = new Size(398, 848);
|
||||
groupBox_SkelList.TabIndex = 0;
|
||||
groupBox_SkelList.TabStop = false;
|
||||
groupBox_SkelList.Text = "模型列表";
|
||||
@@ -315,7 +324,7 @@
|
||||
spineListView.Location = new Point(3, 26);
|
||||
spineListView.Name = "spineListView";
|
||||
spineListView.PropertyGrid = propertyGrid_Spine;
|
||||
spineListView.Size = new Size(321, 819);
|
||||
spineListView.Size = new Size(392, 819);
|
||||
spineListView.TabIndex = 0;
|
||||
//
|
||||
// propertyGrid_Spine
|
||||
@@ -324,7 +333,7 @@
|
||||
propertyGrid_Spine.HelpVisible = false;
|
||||
propertyGrid_Spine.Location = new Point(3, 26);
|
||||
propertyGrid_Spine.Name = "propertyGrid_Spine";
|
||||
propertyGrid_Spine.Size = new Size(407, 470);
|
||||
propertyGrid_Spine.Size = new Size(336, 470);
|
||||
propertyGrid_Spine.TabIndex = 0;
|
||||
propertyGrid_Spine.ToolbarVisible = false;
|
||||
propertyGrid_Spine.PropertyValueChanged += propertyGrid_PropertyValueChanged;
|
||||
@@ -346,7 +355,7 @@
|
||||
//
|
||||
splitContainer_Config.Panel2.Controls.Add(groupBox_PreviewConfig);
|
||||
splitContainer_Config.Panel2.Cursor = Cursors.Default;
|
||||
splitContainer_Config.Size = new Size(413, 848);
|
||||
splitContainer_Config.Size = new Size(342, 848);
|
||||
splitContainer_Config.SplitterDistance = 499;
|
||||
splitContainer_Config.TabIndex = 0;
|
||||
splitContainer_Config.TabStop = false;
|
||||
@@ -359,7 +368,7 @@
|
||||
groupBox_SkelConfig.Dock = DockStyle.Fill;
|
||||
groupBox_SkelConfig.Location = new Point(0, 0);
|
||||
groupBox_SkelConfig.Name = "groupBox_SkelConfig";
|
||||
groupBox_SkelConfig.Size = new Size(413, 499);
|
||||
groupBox_SkelConfig.Size = new Size(342, 499);
|
||||
groupBox_SkelConfig.TabIndex = 0;
|
||||
groupBox_SkelConfig.TabStop = false;
|
||||
groupBox_SkelConfig.Text = "模型参数";
|
||||
@@ -370,7 +379,7 @@
|
||||
groupBox_PreviewConfig.Dock = DockStyle.Fill;
|
||||
groupBox_PreviewConfig.Location = new Point(0, 0);
|
||||
groupBox_PreviewConfig.Name = "groupBox_PreviewConfig";
|
||||
groupBox_PreviewConfig.Size = new Size(413, 345);
|
||||
groupBox_PreviewConfig.Size = new Size(342, 345);
|
||||
groupBox_PreviewConfig.TabIndex = 1;
|
||||
groupBox_PreviewConfig.TabStop = false;
|
||||
groupBox_PreviewConfig.Text = "画面参数";
|
||||
@@ -381,7 +390,7 @@
|
||||
propertyGrid_Previewer.HelpVisible = false;
|
||||
propertyGrid_Previewer.Location = new Point(3, 26);
|
||||
propertyGrid_Previewer.Name = "propertyGrid_Previewer";
|
||||
propertyGrid_Previewer.Size = new Size(407, 316);
|
||||
propertyGrid_Previewer.Size = new Size(336, 316);
|
||||
propertyGrid_Previewer.TabIndex = 1;
|
||||
propertyGrid_Previewer.ToolbarVisible = false;
|
||||
propertyGrid_Previewer.PropertyValueChanged += propertyGrid_PropertyValueChanged;
|
||||
@@ -500,5 +509,6 @@
|
||||
private ToolStripMenuItem toolStripMenuItem_ManageResource;
|
||||
private ToolStripMenuItem toolStripMenuItem_Tool;
|
||||
private ToolStripMenuItem toolStripMenuItem_ConvertFileFormat;
|
||||
private ToolStripMenuItem toolStripMenuItem_ExportPreview;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
using SpineViewer.Spine;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace SpineViewer
|
||||
{
|
||||
@@ -41,6 +43,101 @@ namespace SpineViewer
|
||||
LogManager.ReconfigExistingLoggers();
|
||||
}
|
||||
|
||||
private void MainForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
spinePreviewer.StartPreview();
|
||||
}
|
||||
|
||||
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
|
||||
{
|
||||
spinePreviewer.StopPreview();
|
||||
}
|
||||
|
||||
private void toolStripMenuItem_Open_Click(object sender, EventArgs e)
|
||||
{
|
||||
spineListView.Add();
|
||||
}
|
||||
|
||||
private void toolStripMenuItem_BatchOpen_Click(object sender, EventArgs e)
|
||||
{
|
||||
spineListView.BatchAdd();
|
||||
}
|
||||
|
||||
private void toolStripMenuItem_Export_Click(object sender, EventArgs e)
|
||||
{
|
||||
lock (spineListView.Spines)
|
||||
{
|
||||
if (spineListView.Spines.Count <= 0)
|
||||
{
|
||||
MessageBox.Show("请至少打开一个骨骼文件", "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var exportDialog = new Dialogs.ExportPngDialog();
|
||||
if (exportDialog.ShowDialog() != DialogResult.OK)
|
||||
return;
|
||||
|
||||
var progressDialog = new Dialogs.ProgressDialog();
|
||||
progressDialog.DoWork += ExportPng_Work;
|
||||
progressDialog.RunWorkerAsync(exportDialog);
|
||||
progressDialog.ShowDialog();
|
||||
}
|
||||
|
||||
private void toolStripMenuItem_ExportPreview_Click(object sender, EventArgs e)
|
||||
{
|
||||
spineListView.ExportPreviews();
|
||||
}
|
||||
|
||||
private void toolStripMenuItem_Exit_Click(object sender, EventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
private void toolStripMenuItem_ResetAnimation_Click(object sender, EventArgs e)
|
||||
{
|
||||
lock (spineListView.Spines)
|
||||
{
|
||||
foreach (var spine in spineListView.Spines)
|
||||
spine.CurrentAnimation = spine.CurrentAnimation;
|
||||
}
|
||||
}
|
||||
|
||||
private void toolStripMenuItem_ConvertFileFormat_Click(object sender, EventArgs e)
|
||||
{
|
||||
var openDialog = new Dialogs.ConvertFileFormatDialog();
|
||||
if (openDialog.ShowDialog() != DialogResult.OK)
|
||||
return;
|
||||
|
||||
var progressDialog = new Dialogs.ProgressDialog();
|
||||
progressDialog.DoWork += ConvertFileFormat_Work;
|
||||
progressDialog.RunWorkerAsync(openDialog);
|
||||
progressDialog.ShowDialog();
|
||||
}
|
||||
|
||||
private void toolStripMenuItem_ManageResource_Click(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void toolStripMenuItem_About_Click(object sender, EventArgs e)
|
||||
{
|
||||
(new Dialogs.AboutDialog()).ShowDialog();
|
||||
}
|
||||
|
||||
private void toolStripMenuItem_Diagnostics_Click(object sender, EventArgs e)
|
||||
{
|
||||
(new Dialogs.DiagnosticsDialog()).ShowDialog();
|
||||
}
|
||||
|
||||
private void splitContainer_SplitterMoved(object sender, SplitterEventArgs e) { ActiveControl = null; }
|
||||
|
||||
private void splitContainer_MouseUp(object sender, MouseEventArgs e) { ActiveControl = null; }
|
||||
|
||||
private void propertyGrid_PropertyValueChanged(object sender, PropertyValueChangedEventArgs e) { (sender as PropertyGrid)?.Refresh(); }
|
||||
|
||||
private void spinePreviewer_MouseUp(object sender, MouseEventArgs e) { propertyGrid_Spine.Refresh(); }
|
||||
|
||||
private void ExportPng_Work(object? sender, DoWorkEventArgs e)
|
||||
{
|
||||
var worker = sender as BackgroundWorker;
|
||||
@@ -103,87 +200,31 @@ namespace SpineViewer
|
||||
spinePreviewer.StartPreview();
|
||||
}
|
||||
|
||||
private void MainForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
spinePreviewer.StartPreview();
|
||||
}
|
||||
|
||||
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
|
||||
{
|
||||
spinePreviewer.StopPreview();
|
||||
}
|
||||
|
||||
private void toolStripMenuItem_Open_Click(object sender, EventArgs e)
|
||||
{
|
||||
spineListView.Add();
|
||||
}
|
||||
|
||||
private void toolStripMenuItem_BatchOpen_Click(object sender, EventArgs e)
|
||||
{
|
||||
spineListView.BatchAdd();
|
||||
}
|
||||
|
||||
private void toolStripMenuItem_Export_Click(object sender, EventArgs e)
|
||||
{
|
||||
lock (spineListView.Spines)
|
||||
{
|
||||
if (spineListView.Spines.Count <= 0)
|
||||
{
|
||||
MessageBox.Show("请至少打开一个骨骼文件", "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var exportDialog = new Dialogs.ExportPngDialog();
|
||||
if (exportDialog.ShowDialog() != DialogResult.OK)
|
||||
return;
|
||||
|
||||
var progressDialog = new Dialogs.ProgressDialog();
|
||||
progressDialog.DoWork += ExportPng_Work;
|
||||
progressDialog.RunWorkerAsync(exportDialog);
|
||||
progressDialog.ShowDialog();
|
||||
}
|
||||
|
||||
private void toolStripMenuItem_Exit_Click(object sender, EventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
private void toolStripMenuItem_ResetAnimation_Click(object sender, EventArgs e)
|
||||
{
|
||||
lock (spineListView.Spines)
|
||||
{
|
||||
foreach (var spine in spineListView.Spines)
|
||||
spine.CurrentAnimation = spine.CurrentAnimation;
|
||||
}
|
||||
}
|
||||
|
||||
private void toolStripMenuItem_ConvertFileFormat_Click(object sender, EventArgs e)
|
||||
{
|
||||
var openDialog = new Dialogs.ConvertFileFormatDialog();
|
||||
if (openDialog.ShowDialog() != DialogResult.OK)
|
||||
return;
|
||||
|
||||
var progressDialog = new Dialogs.ProgressDialog();
|
||||
progressDialog.DoWork += ConvertFileFormat_Work;
|
||||
progressDialog.RunWorkerAsync(openDialog);
|
||||
progressDialog.ShowDialog();
|
||||
}
|
||||
|
||||
private void ConvertFileFormat_Work(object? sender, DoWorkEventArgs e)
|
||||
{
|
||||
var worker = sender as BackgroundWorker;
|
||||
var arguments = e.Argument as Dialogs.ConvertFileFormatDialog;
|
||||
var skelPaths = arguments.SkelPaths;
|
||||
var version = arguments.Version;
|
||||
var convertToJson = arguments.ConvertToJson;
|
||||
var newSuffix = convertToJson ? ".json" : ".skel";
|
||||
var srcVersion = arguments.SourceVersion;
|
||||
var tgtVersion = arguments.TargetVersion;
|
||||
var jsonSource = arguments.JsonSource;
|
||||
var jsonTarget = arguments.JsonTarget;
|
||||
var newSuffix = jsonTarget ? ".json" : ".skel";
|
||||
|
||||
if (jsonTarget == jsonSource)
|
||||
{
|
||||
if (tgtVersion == srcVersion)
|
||||
return;
|
||||
else
|
||||
newSuffix += $".{tgtVersion.ToString().ToLower()}"; // TODO: 仅转换版本的情况下考虑文件覆盖问题
|
||||
}
|
||||
|
||||
int totalCount = skelPaths.Length;
|
||||
int success = 0;
|
||||
int error = 0;
|
||||
|
||||
SkeletonConverter cvter = SkeletonConverter.New(version);
|
||||
SkeletonConverter srcCvter = SkeletonConverter.New(srcVersion);
|
||||
SkeletonConverter tgtCvter = tgtVersion == srcVersion ? srcCvter : SkeletonConverter.New(tgtVersion);
|
||||
|
||||
worker.ReportProgress(0, $"已处理 0/{totalCount}");
|
||||
for (int i = 0; i < totalCount; i++)
|
||||
@@ -199,10 +240,9 @@ namespace SpineViewer
|
||||
|
||||
try
|
||||
{
|
||||
if (convertToJson)
|
||||
cvter.BinaryToJson(skelPath, newPath);
|
||||
else
|
||||
cvter.JsonToBinary(skelPath, newPath);
|
||||
var root = jsonSource ? srcCvter.ReadJson(skelPath) : srcCvter.ReadBinary(skelPath);
|
||||
if (tgtVersion != srcVersion) root = srcCvter.ToVersion(root, tgtVersion);
|
||||
if (jsonTarget) tgtCvter.WriteJson(root, newPath); else tgtCvter.WriteBinary(root, newPath);
|
||||
success++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -224,24 +264,5 @@ namespace SpineViewer
|
||||
Program.Logger.Info("{} skel converted successfully", success);
|
||||
}
|
||||
}
|
||||
|
||||
private void toolStripMenuItem_About_Click(object sender, EventArgs e)
|
||||
{
|
||||
(new Dialogs.AboutDialog()).ShowDialog();
|
||||
}
|
||||
|
||||
private void toolStripMenuItem_Diagnostics_Click(object sender, EventArgs e)
|
||||
{
|
||||
(new Dialogs.DiagnosticsDialog()).ShowDialog();
|
||||
}
|
||||
|
||||
private void splitContainer_SplitterMoved(object sender, SplitterEventArgs e) { ActiveControl = null; }
|
||||
|
||||
private void splitContainer_MouseUp(object sender, MouseEventArgs e) { ActiveControl = null; }
|
||||
|
||||
private void propertyGrid_PropertyValueChanged(object sender, PropertyValueChangedEventArgs e) { (sender as PropertyGrid)?.Refresh(); }
|
||||
|
||||
private void spinePreviewer_MouseUp(object sender, MouseEventArgs e) { propertyGrid_Spine.Refresh(); }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using SpineRuntime38;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using SpineRuntime38.Attachments;
|
||||
using System.Globalization;
|
||||
|
||||
namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
{
|
||||
@@ -15,35 +16,38 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
{
|
||||
private SkeletonReader reader = null;
|
||||
private JsonObject root = null;
|
||||
|
||||
private bool nonessential = false;
|
||||
private List<JsonObject> idx2Event = [];
|
||||
|
||||
protected override JsonObject ReadBinary(string binPath)
|
||||
private readonly List<JsonObject> idx2event = [];
|
||||
|
||||
public override JsonObject ReadBinary(string binPath)
|
||||
{
|
||||
var root = new JsonObject();
|
||||
using var input = File.OpenRead(binPath);
|
||||
|
||||
this.root = root;
|
||||
reader = new(input);
|
||||
|
||||
var result = root = [];
|
||||
root["skeleton"] = ReadSkeleton();
|
||||
ReadSkeleton();
|
||||
ReadStrings();
|
||||
root["bones"] = ReadBones();
|
||||
root["slots"] = ReadSlots();
|
||||
root["ik"] = ReadIK();
|
||||
root["transform"] = ReadTransform();
|
||||
root["path"] = ReadPath();
|
||||
root["skins"] = ReadSkins();
|
||||
root["events"] = ReadEvents();
|
||||
root["animations"] = ReadAnimations();
|
||||
ReadBones();
|
||||
ReadSlots();
|
||||
ReadIK();
|
||||
ReadTransform();
|
||||
ReadPath();
|
||||
ReadSkins();
|
||||
ReadEvents();
|
||||
ReadAnimations();
|
||||
|
||||
reader = null;
|
||||
nonessential = false;
|
||||
root = null;
|
||||
this.root = null;
|
||||
|
||||
return result;
|
||||
idx2event.Clear();
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
private JsonObject ReadSkeleton()
|
||||
private void ReadSkeleton()
|
||||
{
|
||||
JsonObject skeleton = [];
|
||||
skeleton["hash"] = reader.ReadString();
|
||||
@@ -59,7 +63,7 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
skeleton["images"] = reader.ReadString();
|
||||
skeleton["audio"] = reader.ReadString();
|
||||
}
|
||||
return skeleton;
|
||||
root["skeleton"] = skeleton;
|
||||
}
|
||||
|
||||
private void ReadStrings()
|
||||
@@ -68,14 +72,14 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
reader.StringTable.Add(reader.ReadString());
|
||||
}
|
||||
|
||||
private JsonArray ReadBones()
|
||||
private void ReadBones()
|
||||
{
|
||||
JsonArray bones = [];
|
||||
for (int i = 0, n = reader.ReadVarInt(); i < n; i++)
|
||||
{
|
||||
JsonObject data = [];
|
||||
data["name"] = reader.ReadString();
|
||||
if (i > 0) data["parent"] = bones[reader.ReadVarInt()]["name"].GetValue<string>();
|
||||
if (i > 0) data["parent"] = (string)bones[reader.ReadVarInt()]["name"];
|
||||
data["rotation"] = reader.ReadFloat();
|
||||
data["x"] = reader.ReadFloat();
|
||||
data["y"] = reader.ReadFloat();
|
||||
@@ -89,10 +93,10 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
if (nonessential) reader.ReadInt();
|
||||
bones.Add(data);
|
||||
}
|
||||
return bones;
|
||||
root["bones"] = bones;
|
||||
}
|
||||
|
||||
private JsonArray ReadSlots()
|
||||
private void ReadSlots()
|
||||
{
|
||||
JsonArray bones = root["bones"].AsArray();
|
||||
JsonArray slots = [];
|
||||
@@ -100,7 +104,7 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
{
|
||||
JsonObject data = [];
|
||||
data["name"] = reader.ReadString();
|
||||
data["bone"] = bones[reader.ReadVarInt()]["name"].GetValue<string>();
|
||||
data["bone"] = (string)bones[reader.ReadVarInt()]["name"];
|
||||
data["color"] = reader.ReadInt().ToString("x8"); // 0xrrggbbaa -> rrggbbaa
|
||||
int dark = reader.ReadInt();
|
||||
if (dark != -1) data["dark"] = dark.ToString("x6"); // 0x00rrggbb -> rrggbb
|
||||
@@ -108,10 +112,10 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
data["blend"] = ((BlendMode)reader.ReadVarInt()).ToString();
|
||||
slots.Add(data);
|
||||
}
|
||||
return slots;
|
||||
root["slots"] = slots;
|
||||
}
|
||||
|
||||
private JsonArray ReadIK()
|
||||
private void ReadIK()
|
||||
{
|
||||
JsonArray bones = root["bones"].AsArray();
|
||||
JsonArray ik = [];
|
||||
@@ -122,7 +126,7 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
data["order"] = reader.ReadVarInt();
|
||||
data["skin"] = reader.ReadBoolean();
|
||||
data["bones"] = ReadNames(bones);
|
||||
data["target"] = bones[reader.ReadVarInt()]["name"].GetValue<string>();
|
||||
data["target"] = (string)bones[reader.ReadVarInt()]["name"];
|
||||
data["mix"] = reader.ReadFloat();
|
||||
data["softness"] = reader.ReadFloat();
|
||||
data["bendPositive"] = reader.ReadSByte() > 0;
|
||||
@@ -131,10 +135,10 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
data["uniform"] = reader.ReadBoolean();
|
||||
ik.Add(data);
|
||||
}
|
||||
return ik;
|
||||
root["ik"] = ik;
|
||||
}
|
||||
|
||||
private JsonArray ReadTransform()
|
||||
private void ReadTransform()
|
||||
{
|
||||
JsonArray bones = root["bones"].AsArray();
|
||||
JsonArray transform = [];
|
||||
@@ -145,7 +149,7 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
data["order"] = reader.ReadVarInt();
|
||||
data["skin"] = reader.ReadBoolean();
|
||||
data["bones"] = ReadNames(bones);
|
||||
data["target"] = bones[reader.ReadVarInt()]["name"].GetValue<string>();
|
||||
data["target"] = (string)bones[reader.ReadVarInt()]["name"];
|
||||
data["local"] = reader.ReadBoolean();
|
||||
data["relative"] = reader.ReadBoolean();
|
||||
data["rotation"] = reader.ReadFloat();
|
||||
@@ -160,10 +164,10 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
data["shearMix"] = reader.ReadFloat();
|
||||
transform.Add(data);
|
||||
}
|
||||
return transform;
|
||||
root["transform"] = transform;
|
||||
}
|
||||
|
||||
private JsonArray ReadPath()
|
||||
private void ReadPath()
|
||||
{
|
||||
JsonArray bones = root["bones"].AsArray();
|
||||
JsonArray path = [];
|
||||
@@ -174,7 +178,7 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
data["order"] = reader.ReadVarInt();
|
||||
data["skin"] = reader.ReadBoolean();
|
||||
data["bones"] = ReadNames(bones);
|
||||
data["target"] = bones[reader.ReadVarInt()]["name"].GetValue<string>();
|
||||
data["target"] = (string)bones[reader.ReadVarInt()]["name"];
|
||||
data["positionMode"] = ((PositionMode)reader.ReadVarInt()).ToString();
|
||||
data["spacingMode"] = ((SpacingMode)reader.ReadVarInt()).ToString();
|
||||
data["rotateMode"] = ((RotateMode)reader.ReadVarInt()).ToString();
|
||||
@@ -185,10 +189,10 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
data["translateMix"] = reader.ReadFloat();
|
||||
path.Add(data);
|
||||
}
|
||||
return path;
|
||||
root["path"] = path;
|
||||
}
|
||||
|
||||
private JsonArray ReadSkins()
|
||||
private void ReadSkins()
|
||||
{
|
||||
JsonArray skins = [];
|
||||
|
||||
@@ -200,7 +204,7 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
for (int n = reader.ReadVarInt(); n > 0; n--)
|
||||
skins.Add(ReadSkin());
|
||||
|
||||
return skins;
|
||||
root["skins"] = skins;
|
||||
}
|
||||
|
||||
private JsonObject? ReadSkin(bool isDefault = false)
|
||||
@@ -229,7 +233,7 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
while (slotCount-- > 0)
|
||||
{
|
||||
JsonObject slotAttachments = [];
|
||||
skinAttachments[slots[reader.ReadVarInt()]["name"].GetValue<string>()] = slotAttachments;
|
||||
skinAttachments[(string)slots[reader.ReadVarInt()]["name"]] = slotAttachments;
|
||||
for (int attachmentCount = reader.ReadVarInt(); attachmentCount > 0; attachmentCount--)
|
||||
{
|
||||
var attachmentKey = reader.ReadStringRef();
|
||||
@@ -321,7 +325,7 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
if (nonessential) reader.ReadInt();
|
||||
break;
|
||||
case AttachmentType.Clipping:
|
||||
attachment["end"] = slots[reader.ReadVarInt()]["name"].GetValue<string>();
|
||||
attachment["end"] = (string)slots[reader.ReadVarInt()]["name"];
|
||||
vertexCount = reader.ReadVarInt();
|
||||
attachment["vertexCount"] = vertexCount;
|
||||
attachment["vertices"] = ReadVertices(vertexCount);
|
||||
@@ -333,9 +337,9 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
return attachment;
|
||||
}
|
||||
|
||||
private JsonObject ReadEvents()
|
||||
private void ReadEvents()
|
||||
{
|
||||
idx2Event.Clear();
|
||||
idx2event.Clear();
|
||||
JsonObject events = [];
|
||||
for (int n = reader.ReadVarInt(); n > 0; n--)
|
||||
{
|
||||
@@ -352,12 +356,12 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
data["volume"] = reader.ReadFloat();
|
||||
data["balance"] = reader.ReadFloat();
|
||||
}
|
||||
idx2Event.Add(data);
|
||||
idx2event.Add(data);
|
||||
}
|
||||
return events;
|
||||
root["events"] = events;
|
||||
}
|
||||
|
||||
private JsonObject ReadAnimations()
|
||||
private void ReadAnimations()
|
||||
{
|
||||
JsonObject animations = [];
|
||||
for (int n = reader.ReadVarInt(); n > 0; n--)
|
||||
@@ -373,7 +377,7 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
if (ReadDrawOrderTimelines() is JsonArray draworder) data["drawOrder"] = draworder;
|
||||
if (ReadEventTimelines() is JsonArray events) data["events"] = events;
|
||||
}
|
||||
return animations;
|
||||
root["animations"] = animations;
|
||||
}
|
||||
|
||||
private JsonObject? ReadSlotTimelines()
|
||||
@@ -384,7 +388,7 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
for (int slotCount = reader.ReadVarInt(); slotCount > 0; slotCount--)
|
||||
{
|
||||
JsonObject timeline = [];
|
||||
slotTimelines[slots[reader.ReadVarInt()]["name"].GetValue<string>()] = timeline;
|
||||
slotTimelines[(string)slots[reader.ReadVarInt()]["name"]] = timeline;
|
||||
for (int timelineCount = reader.ReadVarInt(); timelineCount > 0; timelineCount--)
|
||||
{
|
||||
JsonArray frames = [];
|
||||
@@ -447,7 +451,7 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
for (int boneCount = reader.ReadVarInt(); boneCount > 0; boneCount--)
|
||||
{
|
||||
JsonObject timeline = [];
|
||||
boneTimelines[bones[reader.ReadVarInt()]["name"].GetValue<string>()] = timeline;
|
||||
boneTimelines[(string)bones[reader.ReadVarInt()]["name"]] = timeline;
|
||||
for (int timelineCount = reader.ReadVarInt(); timelineCount > 0; timelineCount--)
|
||||
{
|
||||
JsonArray frames = [];
|
||||
@@ -527,7 +531,7 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
for (int ikCount = reader.ReadVarInt(); ikCount > 0; ikCount--)
|
||||
{
|
||||
JsonArray frames = [];
|
||||
ikTimelines[ik[reader.ReadVarInt()]["name"].GetValue<string>()] = frames;
|
||||
ikTimelines[(string)ik[reader.ReadVarInt()]["name"]] = frames;
|
||||
for (int frameCount = reader.ReadVarInt(); frameCount > 0; frameCount--)
|
||||
{
|
||||
var o = new JsonObject()
|
||||
@@ -555,7 +559,7 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
for (int transformCount = reader.ReadVarInt(); transformCount > 0; transformCount--)
|
||||
{
|
||||
JsonArray frames = [];
|
||||
transformTimelines[transform[reader.ReadVarInt()]["name"].GetValue<string>()] = frames;
|
||||
transformTimelines[(string)transform[reader.ReadVarInt()]["name"]] = frames;
|
||||
for (int frameCount = reader.ReadVarInt(); frameCount > 0; frameCount--)
|
||||
{
|
||||
var o = new JsonObject()
|
||||
@@ -582,7 +586,7 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
for (int pathCount = reader.ReadVarInt(); pathCount > 0; pathCount--)
|
||||
{
|
||||
JsonObject timeline = [];
|
||||
pathTimelines[path[reader.ReadVarInt()]["name"].GetValue<string>()] = timeline;
|
||||
pathTimelines[(string)path[reader.ReadVarInt()]["name"]] = timeline;
|
||||
for (int timelineCount = reader.ReadVarInt(); timelineCount > 0; timelineCount--)
|
||||
{
|
||||
JsonArray frames = [];
|
||||
@@ -643,18 +647,15 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
JsonArray skins = root["skins"].AsArray();
|
||||
JsonObject deformTimelines = [];
|
||||
|
||||
//for (int skinCount = reader.ReadVarInt(); skinCount > 0; skinCount--)
|
||||
for (int i = 0, n = reader.ReadVarInt(); i < n; i++)
|
||||
for (int skinCount = reader.ReadVarInt(); skinCount > 0; skinCount--)
|
||||
{
|
||||
JsonObject skinValue = [];
|
||||
deformTimelines[skins[reader.ReadVarInt()]["name"].GetValue<string>()] = skinValue;
|
||||
//for (int slotCount = reader.ReadVarInt(); slotCount > 0; slotCount--)
|
||||
for (int ii = 0, nn = reader.ReadVarInt(); ii < nn; ii++)
|
||||
deformTimelines[(string)skins[reader.ReadVarInt()]["name"]] = skinValue;
|
||||
for (int slotCount = reader.ReadVarInt(); slotCount > 0; slotCount--)
|
||||
{
|
||||
JsonObject slotValue = [];
|
||||
skinValue[slots[reader.ReadVarInt()]["name"].GetValue<string>()] = slotValue;
|
||||
//for (int attachmentCount = reader.ReadVarInt(); attachmentCount > 0; attachmentCount--)
|
||||
for (int iii = 0, nnn = reader.ReadVarInt(); iii < nnn; iii++)
|
||||
skinValue[(string)slots[reader.ReadVarInt()]["name"]] = slotValue;
|
||||
for (int attachmentCount = reader.ReadVarInt(); attachmentCount > 0; attachmentCount--)
|
||||
{
|
||||
JsonArray frames = [];
|
||||
slotValue[reader.ReadStringRef()] = frames;
|
||||
@@ -698,7 +699,7 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
{
|
||||
offsets.Add(new JsonObject()
|
||||
{
|
||||
["slot"] = slots[reader.ReadVarInt()]["name"].GetValue<string>(),
|
||||
["slot"] = (string)slots[reader.ReadVarInt()]["name"],
|
||||
["offset"] = reader.ReadVarInt(),
|
||||
});
|
||||
}
|
||||
@@ -715,15 +716,15 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
{
|
||||
JsonObject data = [];
|
||||
data["time"] = reader.ReadFloat();
|
||||
JsonObject eventData = idx2Event[reader.ReadVarInt()].AsObject();
|
||||
data["name"] = eventData["name"].GetValue<string>();
|
||||
JsonObject eventData = idx2event[reader.ReadVarInt()].AsObject();
|
||||
data["name"] = (string)eventData["name"];
|
||||
data["int"] = reader.ReadVarInt();
|
||||
data["float"] = reader.ReadFloat();
|
||||
data["string"] = reader.ReadBoolean() ? reader.ReadString() : eventData["string"].GetValue<string>();
|
||||
data["string"] = reader.ReadBoolean() ? reader.ReadString() : (string)eventData["string"];
|
||||
if (eventData.ContainsKey("audio"))
|
||||
{
|
||||
data["volume"] = eventData["volume"].GetValue<string>();
|
||||
data["balance"] = eventData["balance"].GetValue<string>();
|
||||
data["volume"] = (string)eventData["volume"];
|
||||
data["balance"] = (string)eventData["balance"];
|
||||
}
|
||||
eventTimelines.Add(data);
|
||||
}
|
||||
@@ -735,7 +736,7 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
{
|
||||
JsonArray names = [];
|
||||
for (int n = reader.ReadVarInt(); n > 0; n--)
|
||||
names.Add(array[reader.ReadVarInt()]["name"].GetValue<string>());
|
||||
names.Add((string)array[reader.ReadVarInt()]["name"]);
|
||||
return names;
|
||||
}
|
||||
|
||||
@@ -801,9 +802,288 @@ namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
||||
}
|
||||
}
|
||||
|
||||
protected override void WriteBinary(JsonObject root, string binPath)
|
||||
private SkeletonWriter writer;
|
||||
private readonly Dictionary<string, int> bone2idx = [];
|
||||
private readonly Dictionary<string, int> slot2idx = [];
|
||||
private readonly Dictionary<string, int> ik2idx = [];
|
||||
private readonly Dictionary<string, int> transform2idx = [];
|
||||
private readonly Dictionary<string, int> path2idx = [];
|
||||
private readonly Dictionary<string, int> event2idx = [];
|
||||
|
||||
public override void WriteBinary(JsonObject root, string binPath, bool nonessential = false)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
this.nonessential = nonessential;
|
||||
this.root = root;
|
||||
|
||||
using var outputBody = new MemoryStream(); // 先把主体写入内存缓冲区
|
||||
writer = new(outputBody);
|
||||
|
||||
WriteBones();
|
||||
WriteSlots();
|
||||
WriteIK();
|
||||
WriteTransform();
|
||||
WritePath();
|
||||
WriteSkins();
|
||||
WriteEvents();
|
||||
WriteAnimations();
|
||||
|
||||
//using var output = File.Create(binPath); // 将数据写入文件
|
||||
//writer = new(output);
|
||||
|
||||
WriteSkeleton();
|
||||
WriteStrings();
|
||||
//output.Write(outputBody.GetBuffer());
|
||||
|
||||
writer = null;
|
||||
this.root = null;
|
||||
}
|
||||
|
||||
private void WriteSkeleton()
|
||||
{
|
||||
JsonObject skeleton = root["skeleton"].AsObject();
|
||||
writer.WriteString((string)skeleton["hash"]);
|
||||
writer.WriteString((string)skeleton["spine"]);
|
||||
if (skeleton.TryGetPropertyValue("x", out var x)) writer.WriteFloat((float)x); else writer.WriteFloat(0);
|
||||
if (skeleton.TryGetPropertyValue("y", out var y)) writer.WriteFloat((float)y); else writer.WriteFloat(0);
|
||||
if (skeleton.TryGetPropertyValue("width", out var width)) writer.WriteFloat((float)width); else writer.WriteFloat(0);
|
||||
if (skeleton.TryGetPropertyValue("height", out var height)) writer.WriteFloat((float)height); else writer.WriteFloat(0);
|
||||
writer.WriteBoolean(nonessential);
|
||||
if (nonessential)
|
||||
{
|
||||
if (skeleton.TryGetPropertyValue("fps", out var fps)) writer.WriteFloat((float)fps); else writer.WriteFloat(30);
|
||||
if (skeleton.TryGetPropertyValue("images", out var images)) writer.WriteString((string)images); else writer.WriteString(null);
|
||||
if (skeleton.TryGetPropertyValue("audio", out var audio)) writer.WriteString((string)audio); else writer.WriteString(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteStrings()
|
||||
{
|
||||
writer.WriteVarInt(writer.StringTable.Count);
|
||||
foreach (var s in writer.StringTable)
|
||||
writer.WriteString(s);
|
||||
}
|
||||
|
||||
private void WriteBones()
|
||||
{
|
||||
if (!root.ContainsKey("bones"))
|
||||
{
|
||||
writer.WriteVarInt(0);
|
||||
return;
|
||||
}
|
||||
JsonArray bones = root["bones"].AsArray();
|
||||
writer.WriteVarInt(bones.Count);
|
||||
for (int i = 0, n = bones.Count; i < n; i++)
|
||||
{
|
||||
JsonObject data = bones[i].AsObject();
|
||||
var name = (string)data["name"];
|
||||
writer.WriteString(name);
|
||||
if (i > 0) writer.WriteVarInt(bone2idx[(string)data["parent"]]);
|
||||
if (data.TryGetPropertyValue("rotation", out var rotation)) writer.WriteFloat((float)rotation); else writer.WriteFloat(0);
|
||||
if (data.TryGetPropertyValue("x", out var x)) writer.WriteFloat((float)x); else writer.WriteFloat(0);
|
||||
if (data.TryGetPropertyValue("y", out var y)) writer.WriteFloat((float)y); else writer.WriteFloat(0);
|
||||
if (data.TryGetPropertyValue("scaleX", out var scaleX)) writer.WriteFloat((float)scaleX); else writer.WriteFloat(1);
|
||||
if (data.TryGetPropertyValue("scaleY", out var scaleY)) writer.WriteFloat((float)scaleY); else writer.WriteFloat(1);
|
||||
if (data.TryGetPropertyValue("shearX", out var shearX)) writer.WriteFloat((float)shearX); else writer.WriteFloat(0);
|
||||
if (data.TryGetPropertyValue("shearY", out var shearY)) writer.WriteFloat((float)shearY); else writer.WriteFloat(0);
|
||||
if (data.TryGetPropertyValue("length", out var length)) writer.WriteFloat((float)length); else writer.WriteFloat(0);
|
||||
if (data.TryGetPropertyValue("transform", out var transform)) writer.WriteVarInt((int)Enum.Parse<TransformMode>((string)transform, true)); else writer.WriteVarInt((int)TransformMode.Normal);
|
||||
if (data.TryGetPropertyValue("skin", out var skin)) writer.WriteBoolean((bool)skin); else writer.WriteBoolean(false);
|
||||
if (nonessential) writer.WriteInt(0);
|
||||
bone2idx[name] = i;
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteSlots()
|
||||
{
|
||||
if (!root.ContainsKey("slots"))
|
||||
{
|
||||
writer.WriteVarInt(0);
|
||||
return;
|
||||
}
|
||||
JsonArray slots = root["slots"].AsArray();
|
||||
writer.WriteVarInt(slots.Count);
|
||||
for (int i = 0, n = slots.Count; i < n; i++)
|
||||
{
|
||||
JsonObject data = slots[i].AsObject();
|
||||
var name = (string)data["name"];
|
||||
writer.WriteString(name);
|
||||
writer.WriteVarInt(bone2idx[(string)data["bone"]]);
|
||||
if (data.TryGetPropertyValue("color", out var color)) writer.WriteInt(int.Parse((string)color, NumberStyles.HexNumber)); else writer.WriteInt(0);
|
||||
if (data.TryGetPropertyValue("dark", out var dark)) writer.WriteInt(int.Parse((string)dark, NumberStyles.HexNumber)); else writer.WriteInt(-1);
|
||||
if (data.TryGetPropertyValue("attachment", out var attachment)) writer.WriteStringRef((string)attachment); else writer.WriteStringRef(null);
|
||||
if (data.TryGetPropertyValue("blend", out var blend)) writer.WriteVarInt((int)Enum.Parse<BlendMode>((string)blend, true)); else writer.WriteVarInt((int)BlendMode.Normal);
|
||||
slot2idx[name] = i;
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteIK()
|
||||
{
|
||||
if (!root.ContainsKey("ik"))
|
||||
{
|
||||
writer.WriteVarInt(0);
|
||||
return;
|
||||
}
|
||||
JsonArray ik = root["ik"].AsArray();
|
||||
writer.WriteVarInt(ik.Count);
|
||||
for (int i = 0, n = ik.Count; i < n; i++)
|
||||
{
|
||||
JsonObject data = ik[i].AsObject();
|
||||
var name = (string)data["name"];
|
||||
writer.WriteString(name);
|
||||
if (data.TryGetPropertyValue("order", out var order)) writer.WriteVarInt((int)order); else writer.WriteVarInt(0);
|
||||
if (data.TryGetPropertyValue("skin", out var skin)) writer.WriteBoolean((bool)skin); else writer.WriteBoolean(false);
|
||||
if (data.TryGetPropertyValue("bones", out var bones)) WriteNames(bone2idx, bones.AsArray()); else writer.WriteVarInt(0);
|
||||
writer.WriteVarInt(bone2idx[(string)data["target"]]);
|
||||
if (data.TryGetPropertyValue("mix", out var mix)) writer.WriteFloat((float)mix); else writer.WriteFloat(1);
|
||||
if (data.TryGetPropertyValue("softness", out var softness)) writer.WriteFloat((float)softness); else writer.WriteFloat(0);
|
||||
if (data.TryGetPropertyValue("bendPositive", out var bendPositive)) writer.WriteSByte((sbyte)((bool)bendPositive ? 1 : -1)); else writer.WriteSByte(1);
|
||||
if (data.TryGetPropertyValue("compress", out var compress)) writer.WriteBoolean((bool)compress); else writer.WriteBoolean(false);
|
||||
if (data.TryGetPropertyValue("stretch", out var stretch)) writer.WriteBoolean((bool)stretch); else writer.WriteBoolean(false);
|
||||
if (data.TryGetPropertyValue("uniform", out var uniform)) writer.WriteBoolean((bool)uniform); else writer.WriteBoolean(false);
|
||||
ik2idx[name] = i;
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteTransform()
|
||||
{
|
||||
if (!root.ContainsKey("transform"))
|
||||
{
|
||||
writer.WriteVarInt(0);
|
||||
return;
|
||||
}
|
||||
JsonArray transform = root["transform"].AsArray();
|
||||
writer.WriteVarInt(transform.Count);
|
||||
for (int i = 0, n = transform.Count; i < n; i++)
|
||||
{
|
||||
JsonObject data = transform[i].AsObject();
|
||||
var name = (string)data["name"];
|
||||
writer.WriteString(name);
|
||||
if (data.TryGetPropertyValue("order", out var order)) writer.WriteVarInt((int)order); else writer.WriteVarInt(0);
|
||||
if (data.TryGetPropertyValue("skin", out var skin)) writer.WriteBoolean((bool)skin); else writer.WriteBoolean(false);
|
||||
if (data.TryGetPropertyValue("bones", out var bones)) WriteNames(bone2idx, bones.AsArray()); else writer.WriteVarInt(0);
|
||||
writer.WriteVarInt(bone2idx[(string)data["target"]]);
|
||||
if (data.TryGetPropertyValue("local", out var local)) writer.WriteBoolean((bool)local); else writer.WriteBoolean(false);
|
||||
if (data.TryGetPropertyValue("relative", out var relative)) writer.WriteBoolean((bool)relative); else writer.WriteBoolean(false);
|
||||
if (data.TryGetPropertyValue("rotation", out var rotation)) writer.WriteFloat((float)rotation); else writer.WriteFloat(0);
|
||||
if (data.TryGetPropertyValue("x", out var x)) writer.WriteFloat((float)x); else writer.WriteFloat(0);
|
||||
if (data.TryGetPropertyValue("y", out var y)) writer.WriteFloat((float)y); else writer.WriteFloat(0);
|
||||
if (data.TryGetPropertyValue("scaleX", out var scaleX)) writer.WriteFloat((float)scaleX); else writer.WriteFloat(0);
|
||||
if (data.TryGetPropertyValue("scaleY", out var scaleY)) writer.WriteFloat((float)scaleY); else writer.WriteFloat(0);
|
||||
if (data.TryGetPropertyValue("shearY", out var shearY)) writer.WriteFloat((float)shearY); else writer.WriteFloat(0);
|
||||
if (data.TryGetPropertyValue("rotateMix", out var rotateMix)) writer.WriteFloat((float)rotateMix); else writer.WriteFloat(1);
|
||||
if (data.TryGetPropertyValue("translateMix", out var translateMix)) writer.WriteFloat((float)translateMix); else writer.WriteFloat(1);
|
||||
if (data.TryGetPropertyValue("scaleMix", out var scaleMix)) writer.WriteFloat((float)scaleMix); else writer.WriteFloat(1);
|
||||
if (data.TryGetPropertyValue("shearMix", out var shearMix)) writer.WriteFloat((float)shearMix); else writer.WriteFloat(1);
|
||||
transform2idx[name] = i;
|
||||
}
|
||||
}
|
||||
|
||||
private void WritePath()
|
||||
{
|
||||
if (!root.ContainsKey("path"))
|
||||
{
|
||||
writer.WriteVarInt(0);
|
||||
return;
|
||||
}
|
||||
JsonArray path = root["path"].AsArray();
|
||||
writer.WriteVarInt(path.Count);
|
||||
for (int i = 0, n = path.Count; i < n; i++)
|
||||
{
|
||||
JsonObject data = path[i].AsObject();
|
||||
var name = (string)data["name"];
|
||||
writer.WriteString(name);
|
||||
if (data.TryGetPropertyValue("order", out var order)) writer.WriteVarInt((int)order); else writer.WriteVarInt(0);
|
||||
if (data.TryGetPropertyValue("skin", out var skin)) writer.WriteBoolean((bool)skin); else writer.WriteBoolean(false);
|
||||
if (data.TryGetPropertyValue("bones", out var bones)) WriteNames(bone2idx, bones.AsArray()); else writer.WriteVarInt(0);
|
||||
writer.WriteVarInt(bone2idx[(string)data["target"]]);
|
||||
if (data.TryGetPropertyValue("positionMode", out var positionMode)) writer.WriteVarInt((int)Enum.Parse<PositionMode>((string)positionMode, true)); else writer.WriteVarInt((int)PositionMode.Percent);
|
||||
if (data.TryGetPropertyValue("spacingMode", out var spacingMode)) writer.WriteVarInt((int)Enum.Parse<SpacingMode>((string)spacingMode, true)); else writer.WriteVarInt((int)SpacingMode.Length);
|
||||
if (data.TryGetPropertyValue("rotateMode", out var rotateMode)) writer.WriteVarInt((int)Enum.Parse<RotateMode>((string)rotateMode, true)); else writer.WriteVarInt((int)RotateMode.Tangent);
|
||||
if (data.TryGetPropertyValue("rotation", out var rotation)) writer.WriteFloat((float)rotation); else writer.WriteFloat(0);
|
||||
if (data.TryGetPropertyValue("position", out var position)) writer.WriteFloat((float)position); else writer.WriteFloat(0);
|
||||
if (data.TryGetPropertyValue("spacing", out var spacing)) writer.WriteFloat((float)spacing); else writer.WriteFloat(0);
|
||||
if (data.TryGetPropertyValue("rotateMix", out var rotateMix)) writer.WriteFloat((float)rotateMix); else writer.WriteFloat(1);
|
||||
if (data.TryGetPropertyValue("translateMix", out var translateMix)) writer.WriteFloat((float)translateMix); else writer.WriteFloat(1);
|
||||
path2idx[name] = i;
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteSkins()
|
||||
{
|
||||
if (!root.ContainsKey("skins"))
|
||||
{
|
||||
writer.WriteVarInt(0);
|
||||
return;
|
||||
}
|
||||
JsonArray skins = root["skins"].AsArray();
|
||||
writer.WriteVarInt(skins.Count);
|
||||
for (int i = 0, n = skins.Count; i < n; i++)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteEvents()
|
||||
{
|
||||
if (!root.ContainsKey("events"))
|
||||
{
|
||||
writer.WriteVarInt(0);
|
||||
return;
|
||||
}
|
||||
JsonObject events = root["events"].AsObject();
|
||||
writer.WriteVarInt(events.Count);
|
||||
int i = 0;
|
||||
foreach (var (name, _data) in events)
|
||||
{
|
||||
JsonObject data = _data.AsObject();
|
||||
writer.WriteStringRef(name);
|
||||
if (data.TryGetPropertyValue("int", out var @int)) writer.WriteVarInt((int)@int); else writer.WriteVarInt(0);
|
||||
if (data.TryGetPropertyValue("float", out var @float)) writer.WriteFloat((float)@float); else writer.WriteFloat(0);
|
||||
if (data.TryGetPropertyValue("string", out var @string)) writer.WriteString((string)@string); else writer.WriteString("");
|
||||
if (data.TryGetPropertyValue("audio", out var _audio))
|
||||
{
|
||||
var audio = (string)_audio;
|
||||
writer.WriteString(audio);
|
||||
if (audio is not null)
|
||||
{
|
||||
if (data.TryGetPropertyValue("volume", out var volume)) writer.WriteFloat((float)volume); else writer.WriteFloat(1);
|
||||
if (data.TryGetPropertyValue("balance", out var balance)) writer.WriteFloat((float)balance); else writer.WriteFloat(0);
|
||||
}
|
||||
}
|
||||
event2idx[name] = i++;
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteAnimations()
|
||||
{
|
||||
if (!root.ContainsKey("animations"))
|
||||
{
|
||||
writer.WriteVarInt(0);
|
||||
return;
|
||||
}
|
||||
JsonArray animations = root["animations"].AsArray();
|
||||
writer.WriteVarInt(animations.Count);
|
||||
for (int i = 0, n = animations.Count; i < n; i++)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteNames(Dictionary<string, int> name2idx, JsonArray names)
|
||||
{
|
||||
writer.WriteVarInt(names.Count);
|
||||
foreach (var name in names)
|
||||
writer.WriteVarInt(name2idx[(string)name]);
|
||||
}
|
||||
|
||||
public override JsonObject ToVersion(JsonObject root, Version version)
|
||||
{
|
||||
root = version switch
|
||||
{
|
||||
Version.V38 => root.DeepClone().AsObject(),
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
return root;
|
||||
}
|
||||
|
||||
//public void WriteFloatArray(float[] array)
|
||||
|
||||
@@ -13,6 +13,8 @@ namespace SpineViewer.Spine.Implementations.Spine
|
||||
[SpineImplementation(Version.V21)]
|
||||
internal class Spine21 : SpineViewer.Spine.Spine
|
||||
{
|
||||
private static readonly Animation EmptyAnimation = new(EMPTY_ANIMATION, [], 0);
|
||||
|
||||
private class TextureLoader : SpineRuntime21.TextureLoader
|
||||
{
|
||||
public void Load(AtlasPage page, string path)
|
||||
@@ -172,8 +174,15 @@ namespace SpineViewer.Spine.Implementations.Spine
|
||||
|
||||
public override string CurrentAnimation
|
||||
{
|
||||
get => animationState.GetCurrent(0)?.Animation.Name ?? DefaultAnimationName;
|
||||
set { if (animationNames.Contains(value)) { animationState.SetAnimation(0, value, true); Update(0); } }
|
||||
get => animationState.GetCurrent(0)?.Animation.Name ?? EMPTY_ANIMATION;
|
||||
set
|
||||
{
|
||||
if (value == EMPTY_ANIMATION)
|
||||
animationState.SetAnimation(0, EmptyAnimation, false);
|
||||
else if (animationNames.Contains(value))
|
||||
animationState.SetAnimation(0, value, true);
|
||||
Update(0);
|
||||
}
|
||||
}
|
||||
|
||||
public override RectangleF Bounds
|
||||
|
||||
@@ -13,6 +13,8 @@ namespace SpineViewer.Spine.Implementations.Spine
|
||||
[SpineImplementation(Version.V36)]
|
||||
internal class Spine36 : SpineViewer.Spine.Spine
|
||||
{
|
||||
private static readonly Animation EmptyAnimation = new(EMPTY_ANIMATION, [], 0);
|
||||
|
||||
private class TextureLoader : SpineRuntime36.TextureLoader
|
||||
{
|
||||
public void Load(AtlasPage page, string path)
|
||||
@@ -170,8 +172,15 @@ namespace SpineViewer.Spine.Implementations.Spine
|
||||
|
||||
public override string CurrentAnimation
|
||||
{
|
||||
get => animationState.GetCurrent(0)?.Animation.Name ?? DefaultAnimationName;
|
||||
set { if (animationNames.Contains(value)) { animationState.SetAnimation(0, value, true); Update(0); } }
|
||||
get => animationState.GetCurrent(0)?.Animation.Name ?? EMPTY_ANIMATION;
|
||||
set
|
||||
{
|
||||
if (value == EMPTY_ANIMATION)
|
||||
animationState.SetAnimation(0, EmptyAnimation, false);
|
||||
else if (animationNames.Contains(value))
|
||||
animationState.SetAnimation(0, value, true);
|
||||
Update(0);
|
||||
}
|
||||
}
|
||||
|
||||
public override RectangleF Bounds
|
||||
|
||||
@@ -10,6 +10,8 @@ namespace SpineViewer.Spine.Implementations.Spine
|
||||
[SpineImplementation(Version.V37)]
|
||||
internal class Spine37 : SpineViewer.Spine.Spine
|
||||
{
|
||||
private static readonly Animation EmptyAnimation = new(EMPTY_ANIMATION, [], 0);
|
||||
|
||||
private class TextureLoader : SpineRuntime37.TextureLoader
|
||||
{
|
||||
public void Load(AtlasPage page, string path)
|
||||
@@ -177,8 +179,15 @@ namespace SpineViewer.Spine.Implementations.Spine
|
||||
|
||||
public override string CurrentAnimation
|
||||
{
|
||||
get => animationState.GetCurrent(0)?.Animation.Name ?? DefaultAnimationName;
|
||||
set { if (animationNames.Contains(value)) { animationState.SetAnimation(0, value, true); Update(0); } }
|
||||
get => animationState.GetCurrent(0)?.Animation.Name ?? EMPTY_ANIMATION;
|
||||
set
|
||||
{
|
||||
if (value == EMPTY_ANIMATION)
|
||||
animationState.SetAnimation(0, EmptyAnimation, false);
|
||||
else if (animationNames.Contains(value))
|
||||
animationState.SetAnimation(0, value, true);
|
||||
Update(0);
|
||||
}
|
||||
}
|
||||
|
||||
public override RectangleF Bounds
|
||||
|
||||
@@ -13,6 +13,8 @@ namespace SpineViewer.Spine.Implementations.Spine
|
||||
[SpineImplementation(Version.V38)]
|
||||
internal class Spine38 : SpineViewer.Spine.Spine
|
||||
{
|
||||
private static readonly Animation EmptyAnimation = new(EMPTY_ANIMATION, [], 0);
|
||||
|
||||
private class TextureLoader : SpineRuntime38.TextureLoader
|
||||
{
|
||||
public void Load(AtlasPage page, string path)
|
||||
@@ -180,8 +182,15 @@ namespace SpineViewer.Spine.Implementations.Spine
|
||||
|
||||
public override string CurrentAnimation
|
||||
{
|
||||
get => animationState.GetCurrent(0)?.Animation.Name ?? DefaultAnimationName;
|
||||
set { if (animationNames.Contains(value)) { animationState.SetAnimation(0, value, true); Update(0); } }
|
||||
get => animationState.GetCurrent(0)?.Animation.Name ?? EMPTY_ANIMATION;
|
||||
set
|
||||
{
|
||||
if (value == EMPTY_ANIMATION)
|
||||
animationState.SetAnimation(0, EmptyAnimation, false);
|
||||
else if (animationNames.Contains(value))
|
||||
animationState.SetAnimation(0, value, true);
|
||||
Update(0);
|
||||
}
|
||||
}
|
||||
|
||||
public override RectangleF Bounds
|
||||
|
||||
@@ -12,6 +12,8 @@ namespace SpineViewer.Spine.Implementations.Spine
|
||||
[SpineImplementation(Version.V40)]
|
||||
internal class Spine40 : SpineViewer.Spine.Spine
|
||||
{
|
||||
private static readonly Animation EmptyAnimation = new(EMPTY_ANIMATION, [], 0);
|
||||
|
||||
private class TextureLoader : SpineRuntime40.TextureLoader
|
||||
{
|
||||
public void Load(AtlasPage page, string path)
|
||||
@@ -179,8 +181,15 @@ namespace SpineViewer.Spine.Implementations.Spine
|
||||
|
||||
public override string CurrentAnimation
|
||||
{
|
||||
get => animationState.GetCurrent(0)?.Animation.Name ?? DefaultAnimationName;
|
||||
set { if (animationNames.Contains(value)) { animationState.SetAnimation(0, value, true); Update(0); } }
|
||||
get => animationState.GetCurrent(0)?.Animation.Name ?? EMPTY_ANIMATION;
|
||||
set
|
||||
{
|
||||
if (value == EMPTY_ANIMATION)
|
||||
animationState.SetAnimation(0, EmptyAnimation, false);
|
||||
else if (animationNames.Contains(value))
|
||||
animationState.SetAnimation(0, value, true);
|
||||
Update(0);
|
||||
}
|
||||
}
|
||||
|
||||
public override RectangleF Bounds
|
||||
|
||||
@@ -12,6 +12,8 @@ namespace SpineViewer.Spine.Implementations.Spine
|
||||
[SpineImplementation(Version.V41)]
|
||||
internal class Spine41 : SpineViewer.Spine.Spine
|
||||
{
|
||||
private static readonly Animation EmptyAnimation = new(EMPTY_ANIMATION, [], 0);
|
||||
|
||||
private class TextureLoader : SpineRuntime41.TextureLoader
|
||||
{
|
||||
public void Load(AtlasPage page, string path)
|
||||
@@ -179,8 +181,15 @@ namespace SpineViewer.Spine.Implementations.Spine
|
||||
|
||||
public override string CurrentAnimation
|
||||
{
|
||||
get => animationState.GetCurrent(0)?.Animation.Name ?? DefaultAnimationName;
|
||||
set { if (animationNames.Contains(value)) { animationState.SetAnimation(0, value, true); Update(0); } }
|
||||
get => animationState.GetCurrent(0)?.Animation.Name ?? EMPTY_ANIMATION;
|
||||
set
|
||||
{
|
||||
if (value == EMPTY_ANIMATION)
|
||||
animationState.SetAnimation(0, EmptyAnimation, false);
|
||||
else if (animationNames.Contains(value))
|
||||
animationState.SetAnimation(0, value, true);
|
||||
Update(0);
|
||||
}
|
||||
}
|
||||
|
||||
public override RectangleF Bounds
|
||||
|
||||
@@ -12,6 +12,8 @@ namespace SpineViewer.Spine.Implementations.Spine
|
||||
[SpineImplementation(Version.V42)]
|
||||
internal class Spine42 : SpineViewer.Spine.Spine
|
||||
{
|
||||
private static readonly Animation EmptyAnimation = new(EMPTY_ANIMATION, [], 0);
|
||||
|
||||
private class TextureLoader : SpineRuntime42.TextureLoader
|
||||
{
|
||||
public void Load(AtlasPage page, string path)
|
||||
@@ -179,8 +181,15 @@ namespace SpineViewer.Spine.Implementations.Spine
|
||||
|
||||
public override string CurrentAnimation
|
||||
{
|
||||
get => animationState.GetCurrent(0)?.Animation.Name ?? DefaultAnimationName;
|
||||
set { if (animationNames.Contains(value)) { animationState.SetAnimation(0, value, true); Update(0); } }
|
||||
get => animationState.GetCurrent(0)?.Animation.Name ?? EMPTY_ANIMATION;
|
||||
set
|
||||
{
|
||||
if (value == EMPTY_ANIMATION)
|
||||
animationState.SetAnimation(0, EmptyAnimation, false);
|
||||
else if (animationNames.Contains(value))
|
||||
animationState.SetAnimation(0, value, true);
|
||||
Update(0);
|
||||
}
|
||||
}
|
||||
|
||||
public override RectangleF Bounds
|
||||
|
||||
@@ -82,17 +82,17 @@ namespace SpineViewer.Spine
|
||||
/// <summary>
|
||||
/// 读取二进制骨骼文件并构造 Json 对象
|
||||
/// </summary>
|
||||
protected abstract JsonObject ReadBinary(string binPath);
|
||||
public abstract JsonObject ReadBinary(string binPath);
|
||||
|
||||
/// <summary>
|
||||
/// 将 Json 对象写入二进制骨骼文件
|
||||
/// </summary>
|
||||
protected abstract void WriteBinary(JsonObject root, string binPath);
|
||||
public abstract void WriteBinary(JsonObject root, string binPath, bool nonessential = false);
|
||||
|
||||
/// <summary>
|
||||
/// 读取 Json 对象
|
||||
/// </summary>
|
||||
private JsonObject ReadJson(string jsonPath)
|
||||
public JsonObject ReadJson(string jsonPath)
|
||||
{
|
||||
using var input = File.OpenRead(jsonPath);
|
||||
if (JsonNode.Parse(input) is JsonObject root)
|
||||
@@ -104,7 +104,7 @@ namespace SpineViewer.Spine
|
||||
/// <summary>
|
||||
/// 写入 Json 对象
|
||||
/// </summary>
|
||||
private void WriteJson(JsonObject root, string jsonPath)
|
||||
public void WriteJson(JsonObject root, string jsonPath)
|
||||
{
|
||||
using var output = File.Create(jsonPath);
|
||||
using var writer = new Utf8JsonWriter(output, jsonWriterOptions);
|
||||
@@ -112,20 +112,9 @@ namespace SpineViewer.Spine
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 二进制转 Json 格式
|
||||
/// 转换到目标版本
|
||||
/// </summary>
|
||||
public void BinaryToJson(string binPath, string jsonPath)
|
||||
{
|
||||
WriteJson(ReadBinary(binPath), jsonPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Json 转二进制格式
|
||||
/// </summary>
|
||||
public void JsonToBinary(string jsonPath, string binPath)
|
||||
{
|
||||
WriteBinary(ReadJson(jsonPath), binPath);
|
||||
}
|
||||
public abstract JsonObject ToVersion(JsonObject root, Version version);
|
||||
|
||||
protected class SkeletonReader
|
||||
{
|
||||
@@ -234,7 +223,7 @@ namespace SpineViewer.Spine
|
||||
{
|
||||
protected byte[] buffer = new byte[32];
|
||||
protected byte[] bytesBigEndian = new byte[8];
|
||||
public readonly List<string> Strings = new(32);
|
||||
public readonly List<string> StringTable = new(32);
|
||||
protected Stream output;
|
||||
|
||||
public SkeletonWriter(Stream output) { this.output = output; }
|
||||
@@ -323,18 +312,18 @@ namespace SpineViewer.Spine
|
||||
System.Text.Encoding.UTF8.GetBytes(val, 0, val.Length, buffer, 0);
|
||||
WriteFully(buffer, 0, byteCount);
|
||||
}
|
||||
public void WriteStringRef(List<string> strings, string val)
|
||||
public void WriteStringRef(string val)
|
||||
{
|
||||
if (val is null)
|
||||
{
|
||||
WriteVarInt(0);
|
||||
return;
|
||||
}
|
||||
int index = strings.IndexOf(val);
|
||||
int index = StringTable.IndexOf(val);
|
||||
if (index < 0)
|
||||
{
|
||||
strings.Add(val);
|
||||
index = strings.Count - 1;
|
||||
StringTable.Add(val);
|
||||
index = StringTable.Count - 1;
|
||||
}
|
||||
WriteVarInt(index + 1);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,21 @@ namespace SpineViewer.Spine
|
||||
/// </summary>
|
||||
public abstract class Spine : SFML.Graphics.Drawable, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// 空动画标记
|
||||
/// </summary>
|
||||
public const string EMPTY_ANIMATION = "<Empty>";
|
||||
|
||||
/// <summary>
|
||||
/// 预览图大小
|
||||
/// </summary>
|
||||
public static readonly Size PREVIEW_SIZE = new(256, 256);
|
||||
|
||||
/// <summary>
|
||||
/// 缩放最小值
|
||||
/// </summary>
|
||||
public const float SCALE_MIN = 0.001f;
|
||||
|
||||
/// <summary>
|
||||
/// 实现类缓存
|
||||
/// </summary>
|
||||
@@ -103,6 +118,11 @@ namespace SpineViewer.Spine
|
||||
return (Spine)Activator.CreateInstance(spineType, skelPath, atlasPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标识符
|
||||
/// </summary>
|
||||
public readonly string ID = Guid.NewGuid().ToString();
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
@@ -127,13 +147,7 @@ namespace SpineViewer.Spine
|
||||
|
||||
~Spine() { Dispose(false); }
|
||||
public void Dispose() { Dispose(true); GC.SuppressFinalize(this); }
|
||||
protected virtual void Dispose(bool disposing) { }
|
||||
|
||||
/// <summary>
|
||||
/// 缩放最小值
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public const float SCALE_MIN = 0.001f;
|
||||
protected virtual void Dispose(bool disposing) { preview?.Dispose(); }
|
||||
|
||||
/// <summary>
|
||||
/// 获取所属版本
|
||||
@@ -199,7 +213,7 @@ namespace SpineViewer.Spine
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public ReadOnlyCollection<string> AnimationNames { get => animationNames.AsReadOnly(); }
|
||||
protected List<string> animationNames = [];
|
||||
protected List<string> animationNames = [EMPTY_ANIMATION];
|
||||
|
||||
/// <summary>
|
||||
/// 默认动画名称
|
||||
@@ -226,6 +240,61 @@ namespace SpineViewer.Spine
|
||||
[Browsable(false)]
|
||||
public abstract RectangleF Bounds { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 骨骼预览图
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public Image Preview
|
||||
{
|
||||
get
|
||||
{
|
||||
if (preview is null)
|
||||
{
|
||||
using var img = GetPreview((uint)PREVIEW_SIZE.Width, (uint)PREVIEW_SIZE.Height);
|
||||
img.SaveToMemory(out var imgBuffer, "bmp");
|
||||
using var stream = new MemoryStream(imgBuffer);
|
||||
preview = new Bitmap(stream);
|
||||
}
|
||||
return preview;
|
||||
}
|
||||
}
|
||||
private Image preview = null;
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定尺寸的预览图
|
||||
/// </summary>
|
||||
public SFML.Graphics.Image GetPreview(uint width, uint height)
|
||||
{
|
||||
var curAnimation = CurrentAnimation;
|
||||
CurrentAnimation = EMPTY_ANIMATION;
|
||||
var bounds = Bounds;
|
||||
|
||||
float viewX = width;
|
||||
float viewY = height;
|
||||
float sizeX = bounds.Width;
|
||||
float sizeY = bounds.Height;
|
||||
|
||||
var scale = 1f;
|
||||
if ((sizeY / sizeX) < (viewY / viewX))
|
||||
scale = sizeX / viewX;// 相同的 X, 视窗 Y 更大
|
||||
else
|
||||
scale = sizeY / viewY;// 相同的 Y, 视窗 X 更大
|
||||
|
||||
viewX *= scale;
|
||||
viewY *= scale;
|
||||
|
||||
using var tex = new SFML.Graphics.RenderTexture(width, height);
|
||||
var view = tex.GetView();
|
||||
view.Center = new(bounds.X + viewX / 2, bounds.Y + viewY / 2);
|
||||
view.Size = new(viewX, -viewY);
|
||||
tex.SetView(view);
|
||||
tex.Clear(SFML.Graphics.Color.Transparent);
|
||||
tex.Draw(this);
|
||||
tex.Display();
|
||||
CurrentAnimation = curAnimation;
|
||||
return tex.Texture.CopyToImage();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取动画时长, 如果动画不存在则返回 0
|
||||
/// </summary>
|
||||
|
||||
@@ -42,11 +42,7 @@ namespace SpineViewer.Spine
|
||||
public override StandardValuesCollection? GetStandardValues(ITypeDescriptorContext? context)
|
||||
{
|
||||
if (context?.Instance is Spine obj)
|
||||
{
|
||||
// 返回 AnimationNames 作为下拉选项
|
||||
return new StandardValuesCollection(obj.AnimationNames);
|
||||
}
|
||||
|
||||
return base.GetStandardValues(context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<BaseOutputPath>$(SolutionDir)out</BaseOutputPath>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<Version>0.10.0</Version>
|
||||
<Version>0.10.1</Version>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<ApplicationIcon>appicon.ico</ApplicationIcon>
|
||||
|
||||
BIN
img/preview.jpg
Normal file
BIN
img/preview.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 302 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 234 KiB |
Reference in New Issue
Block a user