diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3d0bdd2..658d416 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
# CHANGELOG
+## v0.16.12
+
+- 修复 label 控件文字显示问题
+- 增强报错日志输出
+- 增加实时帧率显示
+- 首选项增加预览画面和投影最大帧率设置,移除用户状态和工作区帧率记忆
+- 优化某些性能
+
## v0.16.11
- 增加 shift 切换缩放倍数
diff --git a/SFMLRenderer/SFMLRenderWindow.cs b/SFMLRenderer/SFMLRenderWindow.cs
index ce31211..41f8327 100644
--- a/SFMLRenderer/SFMLRenderWindow.cs
+++ b/SFMLRenderer/SFMLRenderWindow.cs
@@ -7,6 +7,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Threading;
+using Win32Natives;
namespace SFMLRenderer
{
@@ -19,6 +20,14 @@ namespace SFMLRenderer
SetActive(false);
_timer.Tick += (s, e) => DispatchEvents();
_timer.Start();
+
+ SetVisible(false);
+
+ var handle = SystemHandle;
+ var exStyle = User32.GetWindowLong(handle, User32.GWL_EXSTYLE) | User32.WS_EX_LAYERED;
+ User32.SetWindowLong(handle, User32.GWL_EXSTYLE, exStyle);
+ User32.SetLayeredWindowAttributes(handle, 0, byte.MaxValue, User32.LWA_ALPHA);
+
RendererCreated?.Invoke(this, EventArgs.Empty);
}
diff --git a/SFMLRenderer/SFMLRenderer.csproj b/SFMLRenderer/SFMLRenderer.csproj
index 9d047ed..f05e384 100644
--- a/SFMLRenderer/SFMLRenderer.csproj
+++ b/SFMLRenderer/SFMLRenderer.csproj
@@ -20,4 +20,8 @@
+
+
+
+
diff --git a/Spine/Exporters/CustomFFmpegExporter.cs b/Spine/Exporters/CustomFFmpegExporter.cs
index 7f420c6..016ae49 100644
--- a/Spine/Exporters/CustomFFmpegExporter.cs
+++ b/Spine/Exporters/CustomFFmpegExporter.cs
@@ -92,7 +92,7 @@ namespace Spine.Exporters
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to export {0} {1}, {2}", _format, output, ex.Message);
}
}
diff --git a/Spine/Exporters/FFmpegVideoExporter.cs b/Spine/Exporters/FFmpegVideoExporter.cs
index b52d0ab..860e397 100644
--- a/Spine/Exporters/FFmpegVideoExporter.cs
+++ b/Spine/Exporters/FFmpegVideoExporter.cs
@@ -144,7 +144,7 @@ namespace Spine.Exporters
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to export {0} {1}, {2}", _format, output, ex.Message);
}
}
diff --git a/Spine/Exporters/FrameSequenceExporter.cs b/Spine/Exporters/FrameSequenceExporter.cs
index 9fd035a..f512bc6 100644
--- a/Spine/Exporters/FrameSequenceExporter.cs
+++ b/Spine/Exporters/FrameSequenceExporter.cs
@@ -47,7 +47,7 @@ namespace Spine.Exporters
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to save frame {0}, {1}", savePath, ex.Message);
}
finally
diff --git a/Spine/Implementations/V21/SpineObjectData21.cs b/Spine/Implementations/V21/SpineObjectData21.cs
index d4a39c4..ad009f2 100644
--- a/Spine/Implementations/V21/SpineObjectData21.cs
+++ b/Spine/Implementations/V21/SpineObjectData21.cs
@@ -36,7 +36,7 @@ namespace Spine.Implementations.V21
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
throw new InvalidDataException($"Failed to load atlas '{atlasPath}'");
}
@@ -50,7 +50,7 @@ namespace Spine.Implementations.V21
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath);
}
}
@@ -62,7 +62,7 @@ namespace Spine.Implementations.V21
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath);
}
}
@@ -70,7 +70,7 @@ namespace Spine.Implementations.V21
catch (Exception ex)
{
_atlas.Dispose();
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
throw new InvalidDataException($"Failed to load skeleton file {skelPath}");
}
diff --git a/Spine/Implementations/V34/SpineObjectData34.cs b/Spine/Implementations/V34/SpineObjectData34.cs
index ae31a3a..174040f 100644
--- a/Spine/Implementations/V34/SpineObjectData34.cs
+++ b/Spine/Implementations/V34/SpineObjectData34.cs
@@ -36,7 +36,7 @@ namespace Spine.Implementations.V34
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
throw new InvalidDataException($"Failed to load atlas '{atlasPath}'");
}
@@ -50,7 +50,7 @@ namespace Spine.Implementations.V34
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath);
}
}
@@ -62,7 +62,7 @@ namespace Spine.Implementations.V34
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath);
}
}
@@ -70,7 +70,7 @@ namespace Spine.Implementations.V34
catch (Exception ex)
{
_atlas.Dispose();
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
throw new InvalidDataException($"Failed to load skeleton file {skelPath}");
}
diff --git a/Spine/Implementations/V35/SpineObjectData35.cs b/Spine/Implementations/V35/SpineObjectData35.cs
index 4b29545..23a9a7e 100644
--- a/Spine/Implementations/V35/SpineObjectData35.cs
+++ b/Spine/Implementations/V35/SpineObjectData35.cs
@@ -36,7 +36,7 @@ namespace Spine.Implementations.V35
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
throw new InvalidDataException($"Failed to load atlas '{atlasPath}'");
}
@@ -50,7 +50,7 @@ namespace Spine.Implementations.V35
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath);
}
}
@@ -62,7 +62,7 @@ namespace Spine.Implementations.V35
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath);
}
}
@@ -70,7 +70,7 @@ namespace Spine.Implementations.V35
catch (Exception ex)
{
_atlas.Dispose();
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
throw new InvalidDataException($"Failed to load skeleton file {skelPath}");
}
diff --git a/Spine/Implementations/V36/SpineObjectData36.cs b/Spine/Implementations/V36/SpineObjectData36.cs
index e7846f7..96cbdef 100644
--- a/Spine/Implementations/V36/SpineObjectData36.cs
+++ b/Spine/Implementations/V36/SpineObjectData36.cs
@@ -36,7 +36,7 @@ namespace Spine.Implementations.V36
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
throw new InvalidDataException($"Failed to load atlas '{atlasPath}'");
}
@@ -50,7 +50,7 @@ namespace Spine.Implementations.V36
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath);
}
}
@@ -62,7 +62,7 @@ namespace Spine.Implementations.V36
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath);
}
}
@@ -70,7 +70,7 @@ namespace Spine.Implementations.V36
catch (Exception ex)
{
_atlas.Dispose();
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
throw new InvalidDataException($"Failed to load skeleton file {skelPath}");
}
diff --git a/Spine/Implementations/V37/SpineObjectData37.cs b/Spine/Implementations/V37/SpineObjectData37.cs
index b8674b2..db5ce65 100644
--- a/Spine/Implementations/V37/SpineObjectData37.cs
+++ b/Spine/Implementations/V37/SpineObjectData37.cs
@@ -36,7 +36,7 @@ namespace Spine.Implementations.V37
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
throw new InvalidDataException($"Failed to load atlas '{atlasPath}'");
}
@@ -50,7 +50,7 @@ namespace Spine.Implementations.V37
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath);
}
}
@@ -62,7 +62,7 @@ namespace Spine.Implementations.V37
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath);
}
}
@@ -70,7 +70,7 @@ namespace Spine.Implementations.V37
catch (Exception ex)
{
_atlas.Dispose();
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
throw new InvalidDataException($"Failed to load skeleton file {skelPath}");
}
diff --git a/Spine/Implementations/V38/SpineObjectData38.cs b/Spine/Implementations/V38/SpineObjectData38.cs
index 680d43c..e1069de 100644
--- a/Spine/Implementations/V38/SpineObjectData38.cs
+++ b/Spine/Implementations/V38/SpineObjectData38.cs
@@ -37,7 +37,7 @@ namespace Spine.Implementations.V38
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
throw new InvalidDataException($"Failed to load atlas '{atlasPath}'");
}
@@ -51,7 +51,7 @@ namespace Spine.Implementations.V38
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath);
}
}
@@ -63,7 +63,7 @@ namespace Spine.Implementations.V38
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath);
}
}
@@ -71,7 +71,7 @@ namespace Spine.Implementations.V38
catch (Exception ex)
{
_atlas.Dispose();
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
throw new InvalidDataException($"Failed to load skeleton file {skelPath}");
}
diff --git a/Spine/Implementations/V40/SpineObjectData40.cs b/Spine/Implementations/V40/SpineObjectData40.cs
index b02a489..7df752e 100644
--- a/Spine/Implementations/V40/SpineObjectData40.cs
+++ b/Spine/Implementations/V40/SpineObjectData40.cs
@@ -36,7 +36,7 @@ namespace Spine.Implementations.V40
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
throw new InvalidDataException($"Failed to load atlas '{atlasPath}'");
}
@@ -50,7 +50,7 @@ namespace Spine.Implementations.V40
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath);
}
}
@@ -62,7 +62,7 @@ namespace Spine.Implementations.V40
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath);
}
}
@@ -70,7 +70,7 @@ namespace Spine.Implementations.V40
catch (Exception ex)
{
_atlas.Dispose();
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
throw new InvalidDataException($"Failed to load skeleton file {skelPath}");
}
diff --git a/Spine/Implementations/V41/SpineObjectData41.cs b/Spine/Implementations/V41/SpineObjectData41.cs
index 25ede0d..b1ec552 100644
--- a/Spine/Implementations/V41/SpineObjectData41.cs
+++ b/Spine/Implementations/V41/SpineObjectData41.cs
@@ -36,7 +36,7 @@ namespace Spine.Implementations.V41
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
throw new InvalidDataException($"Failed to load atlas '{atlasPath}'");
}
@@ -50,7 +50,7 @@ namespace Spine.Implementations.V41
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath);
}
}
@@ -62,7 +62,7 @@ namespace Spine.Implementations.V41
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath);
}
}
@@ -70,7 +70,7 @@ namespace Spine.Implementations.V41
catch (Exception ex)
{
_atlas.Dispose();
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
throw new InvalidDataException($"Failed to load skeleton file {skelPath}");
}
diff --git a/Spine/Implementations/V42/SpineObjectData42.cs b/Spine/Implementations/V42/SpineObjectData42.cs
index 95afa35..5a07967 100644
--- a/Spine/Implementations/V42/SpineObjectData42.cs
+++ b/Spine/Implementations/V42/SpineObjectData42.cs
@@ -36,7 +36,7 @@ namespace Spine.Implementations.V42
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
throw new InvalidDataException($"Failed to load atlas '{atlasPath}'");
}
@@ -50,7 +50,7 @@ namespace Spine.Implementations.V42
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath);
}
}
@@ -62,7 +62,7 @@ namespace Spine.Implementations.V42
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath);
}
}
@@ -70,7 +70,7 @@ namespace Spine.Implementations.V42
catch (Exception ex)
{
_atlas.Dispose();
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
throw new InvalidDataException($"Failed to load skeleton file {skelPath}");
}
diff --git a/Spine/SpineExtension.cs b/Spine/SpineExtension.cs
index 9d04db8..c8bc5a6 100644
--- a/Spine/SpineExtension.cs
+++ b/Spine/SpineExtension.cs
@@ -269,7 +269,7 @@ namespace Spine
if (hit && LogHitSlots)
{
- _logger.Debug("Hit ({0}): [{1}]", self.Name, hitSlotName);
+ _logger.Info("Hit ({0}): [{1}]", self.Name, hitSlotName);
}
return hit;
}
diff --git a/Spine/SpineObject.cs b/Spine/SpineObject.cs
index c911521..f1aef2d 100644
--- a/Spine/SpineObject.cs
+++ b/Spine/SpineObject.cs
@@ -82,7 +82,7 @@ namespace Spine
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Warn("Failed to detect version for skel {0}, try all available versions", skelPath);
}
}
@@ -118,7 +118,7 @@ namespace Spine
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
throw new InvalidDataException($"Failed to load spine with version '{version}'");
}
}
diff --git a/SpineViewer.sln b/SpineViewer.sln
index cb6c58c..9cb9c0c 100644
--- a/SpineViewer.sln
+++ b/SpineViewer.sln
@@ -43,6 +43,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpineRuntime35", "SpineRunt
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpineRuntime34", "SpineRuntimes\SpineRuntime34\SpineRuntime34.csproj", "{348605F7-3FF4-1DE0-4B91-7AEFE7BC5C55}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Win32Natives", "Win32Natives\Win32Natives.csproj", "{48864874-7307-950E-A667-62BB66357C62}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -105,6 +107,10 @@ Global
{348605F7-3FF4-1DE0-4B91-7AEFE7BC5C55}.Debug|x64.Build.0 = Debug|x64
{348605F7-3FF4-1DE0-4B91-7AEFE7BC5C55}.Release|x64.ActiveCfg = Release|x64
{348605F7-3FF4-1DE0-4B91-7AEFE7BC5C55}.Release|x64.Build.0 = Release|x64
+ {48864874-7307-950E-A667-62BB66357C62}.Debug|x64.ActiveCfg = Debug|x64
+ {48864874-7307-950E-A667-62BB66357C62}.Debug|x64.Build.0 = Debug|x64
+ {48864874-7307-950E-A667-62BB66357C62}.Release|x64.ActiveCfg = Release|x64
+ {48864874-7307-950E-A667-62BB66357C62}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/SpineViewer/App.xaml.cs b/SpineViewer/App.xaml.cs
index 9baa531..dd403ee 100644
--- a/SpineViewer/App.xaml.cs
+++ b/SpineViewer/App.xaml.cs
@@ -1,7 +1,8 @@
using Microsoft.Win32;
using NLog;
-using SpineViewer.Natives;
+using Win32Natives;
using SpineViewer.Resources;
+using SpineViewer.Services;
using SpineViewer.ViewModels.MainWindow;
using SpineViewer.Views;
using System.Collections.Frozen;
@@ -14,6 +15,7 @@ using System.IO.Pipes;
using System.Reflection;
using System.Windows;
using System.Windows.Interop;
+using SpineViewer.Extensions;
namespace SpineViewer
{
@@ -33,8 +35,8 @@ namespace SpineViewer
#endif
public const string AutoRunFlag = "--autorun";
- private const string MutexName = "__SpineViewerInstance__";
- private const string PipeName = "__SpineViewerPipe__";
+ private const string MutexName = $"__{AppName}_Instance__";
+ private const string PipeName = $"_{AppName}_Pipe__";
public static readonly string ProcessPath = Environment.ProcessPath;
public static readonly string ProcessDirectory = Path.GetDirectoryName(Environment.ProcessPath);
@@ -58,13 +60,16 @@ namespace SpineViewer
AppDomain.CurrentDomain.UnhandledException += (s, e) =>
{
+ _logger.Debug(e.ExceptionObject.ToString());
_logger.Fatal("Unhandled exception: {0}", e.ExceptionObject);
+ MessagePopupService.Error(e.ExceptionObject.ToString());
};
TaskScheduler.UnobservedTaskException += (s, e) =>
{
- _logger.Trace(e.Exception.ToString());
- _logger.Error("Unobserved task exception: {0}", e.Exception.Message);
+ _logger.Debug(e.Exception.ToString());
+ _logger.Fatal("Unobserved task exception: {0}", e.Exception.Message);
e.SetObserved();
+ MessagePopupService.Error(e.Exception.ToString());
};
// 单例模式加 IPC 通信
@@ -130,7 +135,7 @@ namespace SpineViewer
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to pass command line args to existed instance, {0}", ex.Message);
}
}
@@ -191,7 +196,7 @@ namespace SpineViewer
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to process arguments, {0}", ex.Message);
}
}
@@ -212,9 +217,10 @@ namespace SpineViewer
private void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
- _logger.Trace(e.Exception.ToString());
- _logger.Error("Dispatcher unhandled exception: {0}", e.Exception.Message);
+ _logger.Debug(e.Exception.ToString());
+ _logger.Fatal("Dispatcher unhandled exception: {0}", e.Exception.Message);
e.Handled = true;
+ MessagePopupService.Error(e.Exception.ToString());
}
public bool AutoRun
@@ -231,7 +237,7 @@ namespace SpineViewer
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to query autorun registry key, {0}", ex.Message);
return false;
}
@@ -259,7 +265,7 @@ namespace SpineViewer
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to set autorun registry key, {0}", ex.Message);
}
}
@@ -343,7 +349,7 @@ namespace SpineViewer
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to switch language to {0}, {1}", value, ex.Message);
}
}
@@ -360,14 +366,13 @@ namespace SpineViewer
{
Resources.MergedDictionaries.Add(new() { Source = new(uri, UriKind.Relative) });
Resources.MergedDictionaries.Add(new() { Source = new("Resources/Theme.xaml", UriKind.Relative) });
- var hwnd = new WindowInteropHelper(Current.MainWindow).Handle;
- Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText);
- Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region);
+ Current.MainWindow.SetWindowTextColor(AppResource.Color_PrimaryText);
+ Current.MainWindow.SetWindowCaptionColor(AppResource.Color_Region);
_skin = value;
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to switch skin to {0}, {1}", value, ex.Message);
}
}
diff --git a/SpineViewer/Extensions/WpfExtension.cs b/SpineViewer/Extensions/WpfExtension.cs
index 9ce86e0..ddfd3da 100644
--- a/SpineViewer/Extensions/WpfExtension.cs
+++ b/SpineViewer/Extensions/WpfExtension.cs
@@ -1,6 +1,4 @@
-using SFML.Graphics;
-using SFML.System;
-using SkiaSharp;
+using SkiaSharp;
using System;
using System.Collections.Generic;
using System.IO;
@@ -9,29 +7,31 @@ using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
+using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
+using Win32Natives;
namespace SpineViewer.Extensions
{
public static class WpfExtension
{
- public static FloatRect ToFloatRect(this Rect self)
+ public static SFML.Graphics.FloatRect ToFloatRect(this Rect self)
{
return new((float)self.X, (float)self.Y, (float)self.Width, (float)self.Height);
}
- public static Vector2f ToVector2f(this Size self)
+ public static SFML.System.Vector2f ToVector2f(this Size self)
{
return new((float)self.Width, (float)self.Height);
}
- public static Vector2u ToVector2u(this Size self)
+ public static SFML.System.Vector2u ToVector2u(this Size self)
{
return new((uint)self.Width, (uint)self.Height);
}
- public static Vector2i ToVector2i(this Size self)
+ public static SFML.System.Vector2i ToVector2i(this Size self)
{
return new((int)self.Width, (int)self.Height);
}
@@ -60,6 +60,18 @@ namespace SpineViewer.Extensions
return wb;
}
+ public static void SetWindowTextColor(this Window self, Color color)
+ {
+ var hwnd = new WindowInteropHelper(self).Handle;
+ Dwmapi.SetWindowTextColor(hwnd, color.R, color.G, color.B);
+ }
+
+ public static void SetWindowCaptionColor(this Window self, Color color)
+ {
+ var hwnd = new WindowInteropHelper(self).Handle;
+ Dwmapi.SetWindowCaptionColor(hwnd, color.R, color.G, color.B);
+ }
+
//public static void SaveToFile(this BitmapSource bitmap, string path)
//{
// var ext = Path.GetExtension(path)?.ToLowerInvariant();
diff --git a/SpineViewer/Models/PreferenceModel.cs b/SpineViewer/Models/PreferenceModel.cs
index 1575935..7385520 100644
--- a/SpineViewer/Models/PreferenceModel.cs
+++ b/SpineViewer/Models/PreferenceModel.cs
@@ -73,6 +73,22 @@ namespace SpineViewer.Models
#endregion
+ #region 预览画面首选项
+
+ [ObservableProperty]
+ private bool _renderSelectedOnly;
+
+ [ObservableProperty]
+ private HitTestLevel _hitTestLevel;
+
+ [ObservableProperty]
+ private bool _logHitSlots;
+
+ [ObservableProperty]
+ private uint _maxFps = 30;
+
+ #endregion
+
#region 程序选项
public RelayCommand Cmd_SelectAutoRunWorkspaceConfigPath => _cmd_SelectAutoRunWorkspaceConfigPath ??= new(() =>
@@ -89,18 +105,12 @@ namespace SpineViewer.Models
[ObservableProperty]
private AppSkin _appSkin;
- [ObservableProperty]
- private bool _renderSelectedOnly;
-
- [ObservableProperty]
- private HitTestLevel _hitTestLevel;
-
- [ObservableProperty]
- private bool _logHitSlots;
-
[ObservableProperty]
private bool _wallpaperView;
+ [ObservableProperty]
+ private uint _wallpaperMaxFps = 30;
+
[ObservableProperty]
private bool _closeToTray;
diff --git a/SpineViewer/Models/UserStateModel.cs b/SpineViewer/Models/UserStateModel.cs
index 8a0d73f..6ed5643 100644
--- a/SpineViewer/Models/UserStateModel.cs
+++ b/SpineViewer/Models/UserStateModel.cs
@@ -43,7 +43,6 @@ namespace SpineViewer.Models
public uint ResolutionX { get; set; } = 1500;
public uint ResolutionY { get; set; } = 1000;
- public uint MaxFps { get; set; } = 30;
public float Speed { get; set; } = 1f;
public bool ShowAxis { get; set; } = true;
public Color BackgroundColor { get; set; } = Color.FromRgb(105, 105, 105);
diff --git a/SpineViewer/Models/WorkspaceModel.cs b/SpineViewer/Models/WorkspaceModel.cs
index a608cbb..7392d86 100644
--- a/SpineViewer/Models/WorkspaceModel.cs
+++ b/SpineViewer/Models/WorkspaceModel.cs
@@ -34,8 +34,6 @@ namespace SpineViewer.Models
public bool FlipY { get; set; } = true;
- public uint MaxFps { get; set; } = 30;
-
public float Speed { get; set; } = 1f;
public bool ShowAxis { get; set; } = true;
diff --git a/SpineViewer/Resources/Strings/en.xaml b/SpineViewer/Resources/Strings/en.xaml
index e4bdbfd..d584489 100644
--- a/SpineViewer/Resources/Strings/en.xaml
+++ b/SpineViewer/Resources/Strings/en.xaml
@@ -123,6 +123,8 @@
Maximum frame rate of the preview. Set to 0 for no limit.
Playback Speed
Wallpaper View
+ Max FPS of Wallpaper View
+ Maximum frame rate of the wallpaper view. Set to 0 for no limit.
Render Selected Only
Hit Test Accuracy Level
Output Hit Test Slot Names
@@ -140,6 +142,9 @@
Forward 10 Frames
Window/Fullscreen; F11
+
+ Real-time FPS: {0:F1}/{1:F1}
+
OK
Cancel
diff --git a/SpineViewer/Resources/Strings/ja.xaml b/SpineViewer/Resources/Strings/ja.xaml
index 16559c7..37fb083 100644
--- a/SpineViewer/Resources/Strings/ja.xaml
+++ b/SpineViewer/Resources/Strings/ja.xaml
@@ -123,6 +123,8 @@
プレビュー画面の最大フレームレート。0 に設定すると制限なし。
再生速度
壁紙表示
+ 壁紙ビューの最大FPS
+ 壁紙ビューの最大フレームレート。0に設定すると制限がなし。
選択のみレンダリング
ヒットテスト精度レベル
ヒットテスト結果のスロット名を出力
@@ -140,6 +142,9 @@
10フレーム進める
ウィンドウ/フルスクリーン; F11
+
+ リアルタイムFPS:{0:F1}/{1:F1}
+
OK
キャンセル
diff --git a/SpineViewer/Resources/Strings/zh.xaml b/SpineViewer/Resources/Strings/zh.xaml
index 9fc695c..99c1523 100644
--- a/SpineViewer/Resources/Strings/zh.xaml
+++ b/SpineViewer/Resources/Strings/zh.xaml
@@ -123,6 +123,8 @@
预览画面的最大帧率,设置为 0 时则无帧率限制
播放速度
桌面投影
+ 桌面投影最大帧率
+ 桌面投影的最大帧率,设置为 0 时则无帧率限制
仅渲染选中
命中检测准确度等级
输出命中检测结果的插槽名称
@@ -140,6 +142,9 @@
快进 10 帧
窗口/全屏; F11
+
+ 实时帧率:{0:F1}/{1:F1}
+
确认
取消
diff --git a/SpineViewer/Resources/Theme.xaml b/SpineViewer/Resources/Theme.xaml
index fb363f5..c338f5a 100644
--- a/SpineViewer/Resources/Theme.xaml
+++ b/SpineViewer/Resources/Theme.xaml
@@ -11,7 +11,7 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/SpineViewer/SpineViewer.csproj b/SpineViewer/SpineViewer.csproj
index 03b940e..dac2fcd 100644
--- a/SpineViewer/SpineViewer.csproj
+++ b/SpineViewer/SpineViewer.csproj
@@ -8,7 +8,7 @@
net8.0-windows
$(SolutionDir)out
false
- 0.16.11
+ 0.16.12
WinExe
true
@@ -41,5 +41,6 @@
+
diff --git a/SpineViewer/Utils/JsonHelper.cs b/SpineViewer/Utils/JsonHelper.cs
index f37c220..93788b2 100644
--- a/SpineViewer/Utils/JsonHelper.cs
+++ b/SpineViewer/Utils/JsonHelper.cs
@@ -65,7 +65,7 @@ namespace SpineViewer.Utils
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to read json file {0}, {1}", path, ex.Message);
}
}
@@ -86,7 +86,7 @@ namespace SpineViewer.Utils
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to save json file {0}, {1}", path, ex.Message);
return false;
}
@@ -101,7 +101,7 @@ namespace SpineViewer.Utils
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to serialize json object {0}", ex.Message);
return string.Empty;
}
diff --git a/SpineViewer/ViewModels/Exporters/CustomFFmpegExporterViewModel.cs b/SpineViewer/ViewModels/Exporters/CustomFFmpegExporterViewModel.cs
index f1afde6..6ad0c87 100644
--- a/SpineViewer/ViewModels/Exporters/CustomFFmpegExporterViewModel.cs
+++ b/SpineViewer/ViewModels/Exporters/CustomFFmpegExporterViewModel.cs
@@ -112,7 +112,7 @@ namespace SpineViewer.ViewModels.Exporters
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to export {0}, {1}", output, ex.Message);
}
_vmMain.ProgressState = System.Windows.Shell.TaskbarItemProgressState.None;
@@ -169,7 +169,7 @@ namespace SpineViewer.ViewModels.Exporters
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to export {0}, {1}", output, ex.Message);
}
}
diff --git a/SpineViewer/ViewModels/Exporters/FFmpegVideoExporterViewModel.cs b/SpineViewer/ViewModels/Exporters/FFmpegVideoExporterViewModel.cs
index 446639e..f41b325 100644
--- a/SpineViewer/ViewModels/Exporters/FFmpegVideoExporterViewModel.cs
+++ b/SpineViewer/ViewModels/Exporters/FFmpegVideoExporterViewModel.cs
@@ -149,7 +149,7 @@ namespace SpineViewer.ViewModels.Exporters
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to export {0}, {1}", output, ex.Message);
}
_vmMain.ProgressState = System.Windows.Shell.TaskbarItemProgressState.None;
@@ -206,7 +206,7 @@ namespace SpineViewer.ViewModels.Exporters
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to export {0}, {1}", output, ex.Message);
}
}
diff --git a/SpineViewer/ViewModels/Exporters/FrameExporterViewModel.cs b/SpineViewer/ViewModels/Exporters/FrameExporterViewModel.cs
index 839805c..70196e7 100644
--- a/SpineViewer/ViewModels/Exporters/FrameExporterViewModel.cs
+++ b/SpineViewer/ViewModels/Exporters/FrameExporterViewModel.cs
@@ -85,7 +85,7 @@ namespace SpineViewer.ViewModels.Exporters
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to export {0}, {1}", output, ex.Message);
}
}
@@ -121,7 +121,7 @@ namespace SpineViewer.ViewModels.Exporters
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to export {0}, {1}", output, ex.Message);
}
done++;
diff --git a/SpineViewer/ViewModels/Exporters/FrameSequenceExporterViewModel.cs b/SpineViewer/ViewModels/Exporters/FrameSequenceExporterViewModel.cs
index cb547d5..0ef7507 100644
--- a/SpineViewer/ViewModels/Exporters/FrameSequenceExporterViewModel.cs
+++ b/SpineViewer/ViewModels/Exporters/FrameSequenceExporterViewModel.cs
@@ -76,7 +76,7 @@ namespace SpineViewer.ViewModels.Exporters
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to export {0}, {1}", output, ex.Message);
}
_vmMain.ProgressState = System.Windows.Shell.TaskbarItemProgressState.None;
@@ -133,7 +133,7 @@ namespace SpineViewer.ViewModels.Exporters
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to export {0}, {1}", output, ex.Message);
}
}
diff --git a/SpineViewer/ViewModels/MainWindow/ExplorerListViewModel.cs b/SpineViewer/ViewModels/MainWindow/ExplorerListViewModel.cs
index 3a56496..bcb6813 100644
--- a/SpineViewer/ViewModels/MainWindow/ExplorerListViewModel.cs
+++ b/SpineViewer/ViewModels/MainWindow/ExplorerListViewModel.cs
@@ -169,7 +169,7 @@ namespace SpineViewer.ViewModels.MainWindow
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to generate preview: {0}, {1}", m.PreviewFilePath, ex.Message);
}
_logger.LogCurrentProcessMemoryUsage();
@@ -221,7 +221,7 @@ namespace SpineViewer.ViewModels.MainWindow
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to generate preview: {0}, {1}", m.PreviewFilePath, ex.Message);
error++;
}
@@ -261,7 +261,7 @@ namespace SpineViewer.ViewModels.MainWindow
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to delete preview: {0}, {1}", m.PreviewFilePath, ex.Message);
}
}
@@ -302,7 +302,7 @@ namespace SpineViewer.ViewModels.MainWindow
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to delete preview: {0}, {1}", m.PreviewFilePath, ex.Message);
error++;
}
@@ -340,7 +340,7 @@ namespace SpineViewer.ViewModels.MainWindow
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to enumerate files in dir: {0}, {1}", _currentDirectory, ex.Message);
}
}
@@ -408,7 +408,7 @@ namespace SpineViewer.ViewModels.MainWindow
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Warn("Failed to load preview image for {0}, {1}", FullPath, ex.Message);
return null;
}
diff --git a/SpineViewer/ViewModels/MainWindow/PreferenceViewModel.cs b/SpineViewer/ViewModels/MainWindow/PreferenceViewModel.cs
index 5b07df6..989e186 100644
--- a/SpineViewer/ViewModels/MainWindow/PreferenceViewModel.cs
+++ b/SpineViewer/ViewModels/MainWindow/PreferenceViewModel.cs
@@ -5,7 +5,6 @@ using NLog;
using Spine;
using Spine.Implementations;
using SpineViewer.Models;
-using SpineViewer.Natives;
using SpineViewer.Services;
using SpineViewer.Utils;
using System;
@@ -76,7 +75,7 @@ namespace SpineViewer.ViewModels.MainWindow
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to load some prefereneces, {0}", ex.Message);
}
}
@@ -108,12 +107,15 @@ namespace SpineViewer.ViewModels.MainWindow
DebugPoints = DebugPoints,
DebugClippings = DebugClippings,
- AppLanguage = AppLanguage,
- AppSkin = AppSkin,
RenderSelectedOnly = RenderSelectedOnly,
HitTestLevel = HitTestLevel,
LogHitSlots = LogHitSlots,
+ MaxFps = MaxFps,
+
+ AppLanguage = AppLanguage,
+ AppSkin = AppSkin,
WallpaperView = WallpaperView,
+ WallpaperMaxFps = WallpaperMaxFps,
CloseToTray = CloseToTray,
AutoRun = AutoRun,
AutoRunWorkspaceConfigPath = AutoRunWorkspaceConfigPath,
@@ -140,12 +142,15 @@ namespace SpineViewer.ViewModels.MainWindow
DebugPoints = value.DebugPoints;
DebugClippings = value.DebugClippings;
- AppLanguage = value.AppLanguage;
- AppSkin = value.AppSkin;
RenderSelectedOnly = value.RenderSelectedOnly;
HitTestLevel = value.HitTestLevel;
LogHitSlots = value.LogHitSlots;
+ MaxFps = value.MaxFps;
+
+ AppLanguage = value.AppLanguage;
+ AppSkin = value.AppSkin;
WallpaperView = value.WallpaperView;
+ WallpaperMaxFps = value.WallpaperMaxFps;
CloseToTray = value.CloseToTray;
AutoRun = value.AutoRun;
AutoRunWorkspaceConfigPath = value.AutoRunWorkspaceConfigPath;
@@ -251,26 +256,10 @@ namespace SpineViewer.ViewModels.MainWindow
#endregion
- #region 程序选项
-
- public static ImmutableArray AppLanguageOptions { get; } = Enum.GetValues().ToImmutableArray();
-
- public static ImmutableArray AppSkinOptions { get; } = Enum.GetValues().ToImmutableArray();
+ #region 预览画面首选项
public static ImmutableArray HitTestLevelOptions { get; } = Enum.GetValues().ToImmutableArray();
- public AppLanguage AppLanguage
- {
- get => ((App)App.Current).Language;
- set => SetProperty(((App)App.Current).Language, value, v => ((App)App.Current).Language = v);
- }
-
- public AppSkin AppSkin
- {
- get => ((App)App.Current).Skin;
- set => SetProperty(((App)App.Current).Skin, value, v => ((App)App.Current).Skin = v);
- }
-
public bool RenderSelectedOnly
{
get => _vmMain.SFMLRendererViewModel.RenderSelectedOnly;
@@ -289,12 +278,44 @@ namespace SpineViewer.ViewModels.MainWindow
set => SetProperty(SpineExtension.LogHitSlots, value, v => SpineExtension.LogHitSlots = v);
}
+ public uint MaxFps
+ {
+ get => _vmMain.SFMLRendererViewModel.MaxFps;
+ set => SetProperty(_vmMain.SFMLRendererViewModel.MaxFps, value, v => _vmMain.SFMLRendererViewModel.MaxFps = v);
+ }
+
+ #endregion
+
+ #region 程序选项
+
+ public static ImmutableArray AppLanguageOptions { get; } = Enum.GetValues().ToImmutableArray();
+
+ public static ImmutableArray AppSkinOptions { get; } = Enum.GetValues().ToImmutableArray();
+
+ public AppLanguage AppLanguage
+ {
+ get => ((App)App.Current).Language;
+ set => SetProperty(((App)App.Current).Language, value, v => ((App)App.Current).Language = v);
+ }
+
+ public AppSkin AppSkin
+ {
+ get => ((App)App.Current).Skin;
+ set => SetProperty(((App)App.Current).Skin, value, v => ((App)App.Current).Skin = v);
+ }
+
public bool WallpaperView
{
get => _vmMain.SFMLRendererViewModel.WallpaperView;
set => SetProperty(_vmMain.SFMLRendererViewModel.WallpaperView, value, v => _vmMain.SFMLRendererViewModel.WallpaperView = v);
}
+ public uint WallpaperMaxFps
+ {
+ get => _vmMain.SFMLRendererViewModel.WallpaperMaxFps;
+ set => SetProperty(_vmMain.SFMLRendererViewModel.WallpaperMaxFps, value, v => _vmMain.SFMLRendererViewModel.WallpaperMaxFps = v);
+ }
+
public bool CloseToTray
{
get => _vmMain.CloseToTray;
diff --git a/SpineViewer/ViewModels/MainWindow/SFMLRendererViewModel.cs b/SpineViewer/ViewModels/MainWindow/SFMLRendererViewModel.cs
index e70f138..9a5ea91 100644
--- a/SpineViewer/ViewModels/MainWindow/SFMLRendererViewModel.cs
+++ b/SpineViewer/ViewModels/MainWindow/SFMLRendererViewModel.cs
@@ -50,7 +50,7 @@ namespace SpineViewer.ViewModels.MainWindow
///
/// 坐标轴顶点缓冲区
///
- private readonly SFML.Graphics.VertexArray _axisVertices = new(SFML.Graphics.PrimitiveType.Lines, 2); // XXX: 暂时未使用 Dispose 释放
+ private readonly SFML.Graphics.VertexArray _axisVertices = new(SFML.Graphics.PrimitiveType.Lines, 4); // XXX: 暂时未使用 Dispose 释放
///
/// 帧间隔计时器
@@ -61,6 +61,7 @@ namespace SpineViewer.ViewModels.MainWindow
/// 渲染任务
///
private Task? _renderTask = null;
+ private Task? _wallpaperRenderTask = null;
private CancellationTokenSource? _cancelToken = null;
///
@@ -87,6 +88,12 @@ namespace SpineViewer.ViewModels.MainWindow
_models = _vmMain.SpineObjects;
_renderer = _vmMain.SFMLRenderer;
_wallpaperRenderer = _vmMain.WallpaperRenderer;
+
+ // 画一个很长的坐标轴, 用 1e9 比较合适
+ _axisVertices[0] = new(new(-1e9f, 0), _axisColor);
+ _axisVertices[1] = new(new(1e9f, 0), _axisColor);
+ _axisVertices[2] = new(new(0, -1e9f), _axisColor);
+ _axisVertices[3] = new(new(0, 1e9f), _axisColor);
}
///
@@ -153,9 +160,27 @@ namespace SpineViewer.ViewModels.MainWindow
public uint MaxFps
{
get => _renderer.MaxFps;
- set => SetProperty(_renderer.MaxFps, value, v => _renderer.MaxFps = _wallpaperRenderer.MaxFps = value);
+ set => SetProperty(_renderer.MaxFps, value, v => _renderer.MaxFps = value);
}
+ public uint WallpaperMaxFps
+ {
+ get => _wallpaperRenderer.MaxFps;
+ set => SetProperty(_wallpaperRenderer.MaxFps, value, v => _wallpaperRenderer.MaxFps = value);
+ }
+
+ public float RealTimeFps => _realTimeFps;
+ private float _realTimeFps;
+
+ private float _accumFpsTime;
+ private int _accumFpsCount;
+
+ public float WallpaperRealTimeFps => _wallpaperRealTimeFps;
+ private float _wallpaperRealTimeFps;
+
+ private int _accumWallpaperFpsCount;
+ private readonly object _accumWallpaperFpsCountLock = new();
+
public float Speed
{
get => _speed;
@@ -188,7 +213,7 @@ namespace SpineViewer.ViewModels.MainWindow
///
private SFML.Graphics.Color _axisColor = SFML.Graphics.Color.White;
- public string BackgroundImagePath
+ public string? BackgroundImagePath
{
get => _backgroundImagePath;
set => SetProperty(_backgroundImagePath, value, v =>
@@ -237,7 +262,7 @@ namespace SpineViewer.ViewModels.MainWindow
}
});
}
- private string _backgroundImagePath;
+ private string? _backgroundImagePath;
public Stretch BackgroundImageMode
{
@@ -447,26 +472,29 @@ namespace SpineViewer.ViewModels.MainWindow
{
if (_renderTask is not null) return;
_cancelToken = new();
- _renderTask = new Task(RenderTask, _cancelToken.Token, TaskCreationOptions.LongRunning);
+ _renderTask = new(RenderTask, _cancelToken.Token, TaskCreationOptions.LongRunning);
+ _wallpaperRenderTask = new(WallpaperRenderTask, _cancelToken.Token, TaskCreationOptions.LongRunning);
_renderTask.Start();
+ _wallpaperRenderTask.Start();
IsUpdating = true;
}
public void StopRender()
{
IsUpdating = false;
- if (_renderTask is null || _cancelToken is null) return;
+ if (_cancelToken is null || _renderTask is null || _wallpaperRenderTask is null) return;
_cancelToken.Cancel();
+ _wallpaperRenderTask.Wait();
_renderTask.Wait();
- _cancelToken = null;
+ _wallpaperRenderTask = null;
_renderTask = null;
+ _cancelToken = null;
}
private void RenderTask()
{
try
{
- _wallpaperRenderer.SetActive(true);
_renderer.SetActive(true);
float delta;
@@ -475,111 +503,211 @@ namespace SpineViewer.ViewModels.MainWindow
delta = _clock.ElapsedTime.AsSeconds();
_clock.Restart();
- // 停止更新的时候只是时间不前进, 但是坐标变换还是要更新, 否则无法移动对象
- if (!_isUpdating) delta = 0;
+ UpdateLogicFrame(delta);
+ UpdateRenderFrame();
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.Debug(ex.ToString());
+ _logger.Fatal("Render task stopped, {0}", ex.Message);
+ MessagePopupService.Error(ex.ToString());
+ }
+ finally
+ {
+ _renderer.SetActive(false);
+ }
+ }
- // 加上要快进的量
- lock (_forwardDeltaLock)
+ private void UpdateLogicFrame(float delta)
+ {
+ // 计算实时帧率, 1 秒刷新一次
+ _accumFpsTime += delta;
+ if (_accumFpsTime > 1f)
+ {
+ _realTimeFps = _accumFpsCount / _accumFpsTime;
+ _accumFpsCount = 0;
+
+ lock (_accumWallpaperFpsCountLock)
+ {
+ _wallpaperRealTimeFps = _accumWallpaperFpsCount / _accumFpsTime;
+ _accumWallpaperFpsCount = 0;
+ }
+
+ _accumFpsTime = 0f;
+ OnPropertyChanged(nameof(RealTimeFps));
+ OnPropertyChanged(nameof(WallpaperRealTimeFps));
+ }
+
+ // 停止更新的时候只是时间不前进, 但是坐标变换还是要更新, 否则无法移动对象
+ if (!_isUpdating) delta = 0;
+
+ // 加上要快进的量
+ lock (_forwardDeltaLock)
+ {
+ delta += _forwardDelta;
+ _forwardDelta = 0;
+ }
+
+ // 更新模型对象时间
+ lock (_models.Lock)
+ {
+ foreach (var sp in _models.Where(sp => sp.IsShown && (!_renderSelectedOnly || sp.IsSelected)).Reverse())
+ {
+ if (_cancelToken?.IsCancellationRequested ?? true) break; // 提前中止
+
+ sp.Update(0); // 避免物理效果出现问题
+ sp.Update(delta * _speed);
+ }
+ }
+
+ // 更新背景图位置和缩放
+ lock (_bgLock)
+ {
+ if (_backgroundImageSprite is not null)
+ {
+ using var view = _renderer.GetView();
+ var bg = _backgroundImageSprite;
+ var viewSize = view.Size;
+ var bgSize = bg.Texture.Size;
+ var scaleX = Math.Abs(viewSize.X / bgSize.X);
+ var scaleY = Math.Abs(viewSize.Y / bgSize.Y);
+ var signX = Math.Sign(viewSize.X);
+ var signY = Math.Sign(viewSize.Y);
+ if (_backgroundImageMode == Stretch.None)
{
- delta += _forwardDelta;
- _forwardDelta = 0;
+ scaleX = scaleY = 1f / _renderer.Zoom;
+ }
+ else if (_backgroundImageMode == Stretch.Uniform)
+ {
+ scaleX = scaleY = Math.Min(scaleX, scaleY);
+ }
+ else if (_backgroundImageMode == Stretch.UniformToFill)
+ {
+ scaleX = scaleY = Math.Max(scaleX, scaleY);
+ }
+ bg.Scale = new(signX * scaleX, signY * scaleY);
+ bg.Position = view.Center;
+ bg.Rotation = view.Rotation;
+ }
+ }
+ }
+
+ private void UpdateRenderFrame()
+ {
+ if (!_vmMain.IsVisible)
+ {
+ // 必须休眠一会, 否则会空转影响整体渲染循环
+ Thread.Sleep(1);
+ return;
+ }
+
+ // 清除背景
+ _renderer.Clear(_backgroundColor);
+
+ // 渲染背景
+ lock (_bgLock)
+ {
+ if (_backgroundImageSprite is not null)
+ {
+ _renderer.Draw(_backgroundImageSprite);
+ }
+ }
+
+ // 渲染坐标轴
+ if (_showAxis)
+ {
+ _renderer.Draw(_axisVertices);
+ }
+
+ // 渲染 Spine
+ lock (_models.Lock)
+ {
+ foreach (var sp in _models.Where(sp => sp.IsShown && (!_renderSelectedOnly || sp.IsSelected)).Reverse())
+ {
+ if (_cancelToken?.IsCancellationRequested ?? true) break; // 提前中止
+
+ // 为选中对象绘制一个半透明背景
+ if (sp.IsSelected)
+ {
+ var rc = sp.GetCurrentBounds().ToFloatRect();
+ _selectedBackgroundVertices[0] = new(new(rc.Left, rc.Top), _selectedBackgroundColor);
+ _selectedBackgroundVertices[1] = new(new(rc.Left + rc.Width, rc.Top), _selectedBackgroundColor);
+ _selectedBackgroundVertices[2] = new(new(rc.Left + rc.Width, rc.Top + rc.Height), _selectedBackgroundColor);
+ _selectedBackgroundVertices[3] = new(new(rc.Left, rc.Top + rc.Height), _selectedBackgroundColor);
+ _renderer.Draw(_selectedBackgroundVertices);
}
+ // 仅在预览画面临时启用调试模式
+ sp.EnableDebug = true;
+ _renderer.Draw(sp);
+ sp.EnableDebug = false;
+ }
+ }
+
+ // 显示内容
+ _renderer.Display();
+
+ // 帧数加一
+ _accumFpsCount++;
+ }
+
+ private void WallpaperRenderTask()
+ {
+ try
+ {
+ _wallpaperRenderer.SetActive(true);
+ while (!_cancelToken?.IsCancellationRequested ?? false)
+ {
+ if (!_wallpaperView)
+ {
+ Thread.Sleep(10);
+ continue;
+ }
+
+ // 同步视图
using var view = _renderer.GetView();
_wallpaperRenderer.SetView(view);
- if (_vmMain.IsVisible) _renderer.Clear(_backgroundColor);
- if (_wallpaperView) _wallpaperRenderer.Clear(_backgroundColor);
+ // 清除背景
+ _wallpaperRenderer.Clear(_backgroundColor);
// 渲染背景
lock (_bgLock)
{
if (_backgroundImageSprite is not null)
{
- var bg = _backgroundImageSprite;
- var viewSize = view.Size;
- var bgSize = bg.Texture.Size;
- var scaleX = Math.Abs(viewSize.X / bgSize.X);
- var scaleY = Math.Abs(viewSize.Y / bgSize.Y);
- var signX = Math.Sign(viewSize.X);
- var signY = Math.Sign(viewSize.Y);
- if (_backgroundImageMode == Stretch.None)
- {
- scaleX = scaleY = 1f / _renderer.Zoom;
- }
- else if (_backgroundImageMode == Stretch.Uniform)
- {
- scaleX = scaleY = Math.Min(scaleX, scaleY);
- }
- else if (_backgroundImageMode == Stretch.UniformToFill)
- {
- scaleX = scaleY = Math.Max(scaleX, scaleY);
- }
- bg.Scale = new(signX * scaleX, signY * scaleY);
- bg.Position = view.Center;
- bg.Rotation = view.Rotation;
-
- if (_vmMain.IsVisible) _renderer.Draw(bg);
- if (_wallpaperView) _wallpaperRenderer.Draw(bg);
+ _wallpaperRenderer.Draw(_backgroundImageSprite);
}
}
- if (_showAxis && _vmMain.IsVisible)
- {
- // 画一个很长的坐标轴, 用 1e9 比较合适
- _axisVertices[0] = new(new(-1e9f, 0), _axisColor);
- _axisVertices[1] = new(new(1e9f, 0), _axisColor);
- _renderer.Draw(_axisVertices);
- _axisVertices[0] = new(new(0, -1e9f), _axisColor);
- _axisVertices[1] = new(new(0, 1e9f), _axisColor);
- _renderer.Draw(_axisVertices);
- }
-
// 渲染 Spine
lock (_models.Lock)
{
foreach (var sp in _models.Where(sp => sp.IsShown && (!_renderSelectedOnly || sp.IsSelected)).Reverse())
{
- if (_cancelToken?.IsCancellationRequested ?? true) break; // 提前中止
+ if (_cancelToken?.IsCancellationRequested ?? true)
+ break; // 提前中止
- sp.Update(0); // 避免物理效果出现问题
- sp.Update(delta * _speed);
-
- if (_vmMain.IsVisible)
- {
- // 为选中对象绘制一个半透明背景
- if (sp.IsSelected)
- {
- var rc = sp.GetCurrentBounds().ToFloatRect();
- _selectedBackgroundVertices[0] = new(new(rc.Left, rc.Top), _selectedBackgroundColor);
- _selectedBackgroundVertices[1] = new(new(rc.Left + rc.Width, rc.Top), _selectedBackgroundColor);
- _selectedBackgroundVertices[2] = new(new(rc.Left + rc.Width, rc.Top + rc.Height), _selectedBackgroundColor);
- _selectedBackgroundVertices[3] = new(new(rc.Left, rc.Top + rc.Height), _selectedBackgroundColor);
- _renderer.Draw(_selectedBackgroundVertices);
- }
-
- // 仅在预览画面临时启用调试模式
- sp.EnableDebug = true;
- _renderer.Draw(sp);
- sp.EnableDebug = false;
- }
- if (_wallpaperView) _wallpaperRenderer.Draw(sp);
+ _wallpaperRenderer.Draw(sp);
}
}
- if (_vmMain.IsVisible) _renderer.Display();
- if (_wallpaperView) _wallpaperRenderer.Display();
+ // 显示渲染
+ _wallpaperRenderer.Display();
+
+ // 帧数加一
+ lock (_accumWallpaperFpsCountLock) _accumWallpaperFpsCount++;
}
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
- _logger.Fatal("Render task stopped, {0}", ex.Message);
+ _logger.Debug(ex.ToString());
+ _logger.Fatal("Wallpaper render task stopped, {0}", ex.Message);
MessagePopupService.Error(ex.ToString());
}
finally
{
- _renderer.SetActive(false);
_wallpaperRenderer.SetActive(false);
}
}
@@ -598,7 +726,6 @@ namespace SpineViewer.ViewModels.MainWindow
Rotation = Rotation,
FlipX = FlipX,
FlipY = FlipY,
- MaxFps = MaxFps,
Speed = Speed,
ShowAxis = ShowAxis,
BackgroundColor = BackgroundColor,
@@ -615,7 +742,6 @@ namespace SpineViewer.ViewModels.MainWindow
Rotation = value.Rotation;
FlipX = value.FlipX;
FlipY = value.FlipY;
- MaxFps = value.MaxFps;
Speed = value.Speed;
ShowAxis = value.ShowAxis;
BackgroundColor = value.BackgroundColor;
diff --git a/SpineViewer/ViewModels/MainWindow/SpineObjectListViewModel.cs b/SpineViewer/ViewModels/MainWindow/SpineObjectListViewModel.cs
index a6cda79..eb325f1 100644
--- a/SpineViewer/ViewModels/MainWindow/SpineObjectListViewModel.cs
+++ b/SpineViewer/ViewModels/MainWindow/SpineObjectListViewModel.cs
@@ -213,7 +213,7 @@ namespace SpineViewer.ViewModels.MainWindow
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to load: {0}, {1}", skelPath, ex.Message);
}
return false;
@@ -340,7 +340,7 @@ namespace SpineViewer.ViewModels.MainWindow
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to reload spine {0}, {1}", sp.SkelPath, ex.Message);
}
}
@@ -401,7 +401,7 @@ namespace SpineViewer.ViewModels.MainWindow
catch (Exception ex)
{
error++;
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to reload spine {0}, {1}", sp.SkelPath, ex.Message);
}
}
@@ -718,7 +718,7 @@ namespace SpineViewer.ViewModels.MainWindow
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to load: {0}, {1}", cfg.SkelPath, ex.Message);
}
return false;
diff --git a/SpineViewer/ViewModels/ProgressDialogViewModel.cs b/SpineViewer/ViewModels/ProgressDialogViewModel.cs
index 7bc3f9f..6c2a9cc 100644
--- a/SpineViewer/ViewModels/ProgressDialogViewModel.cs
+++ b/SpineViewer/ViewModels/ProgressDialogViewModel.cs
@@ -35,7 +35,7 @@ namespace SpineViewer.ViewModels
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Error("Failed to finish work: {0}, {1}", _title, ex.Message);
WorkFinished?.Invoke(this, false);
}
diff --git a/SpineViewer/Views/AboutDialog.xaml b/SpineViewer/Views/AboutDialog.xaml
index d11fcd2..c9a6255 100644
--- a/SpineViewer/Views/AboutDialog.xaml
+++ b/SpineViewer/Views/AboutDialog.xaml
@@ -19,7 +19,7 @@
-
diff --git a/SpineViewer/Views/AboutDialog.xaml.cs b/SpineViewer/Views/AboutDialog.xaml.cs
index f390768..a98bc35 100644
--- a/SpineViewer/Views/AboutDialog.xaml.cs
+++ b/SpineViewer/Views/AboutDialog.xaml.cs
@@ -1,4 +1,4 @@
-using SpineViewer.Natives;
+using SpineViewer.Extensions;
using SpineViewer.Resources;
using System;
using System.Collections.Generic;
@@ -14,6 +14,7 @@ using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
+using Win32Natives;
namespace SpineViewer.Views
{
@@ -30,9 +31,8 @@ namespace SpineViewer.Views
private void AboutDialog_SourceInitialized(object? sender, EventArgs e)
{
- var hwnd = new WindowInteropHelper(this).Handle;
- Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText);
- Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region);
+ this.SetWindowTextColor(AppResource.Color_PrimaryText);
+ this.SetWindowCaptionColor(AppResource.Color_Region);
}
}
}
diff --git a/SpineViewer/Views/DiagnosticsDialog.xaml b/SpineViewer/Views/DiagnosticsDialog.xaml
index ea046ad..ba58e9b 100644
--- a/SpineViewer/Views/DiagnosticsDialog.xaml
+++ b/SpineViewer/Views/DiagnosticsDialog.xaml
@@ -24,7 +24,7 @@
-
diff --git a/SpineViewer/Views/DiagnosticsDialog.xaml.cs b/SpineViewer/Views/DiagnosticsDialog.xaml.cs
index 72ca5aa..c79e895 100644
--- a/SpineViewer/Views/DiagnosticsDialog.xaml.cs
+++ b/SpineViewer/Views/DiagnosticsDialog.xaml.cs
@@ -1,4 +1,4 @@
-using SpineViewer.Natives;
+using SpineViewer.Extensions;
using SpineViewer.Resources;
using System;
using System.Collections.Generic;
@@ -14,6 +14,7 @@ using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
+using Win32Natives;
namespace SpineViewer.Views
{
@@ -30,9 +31,8 @@ namespace SpineViewer.Views
private void DiagnosticsDialog_SourceInitialized(object? sender, EventArgs e)
{
- var hwnd = new WindowInteropHelper(this).Handle;
- Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText);
- Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region);
+ this.SetWindowTextColor(AppResource.Color_PrimaryText);
+ this.SetWindowCaptionColor(AppResource.Color_Region);
}
}
}
diff --git a/SpineViewer/Views/ExporterDialogs/CustomFFmpegExporterDialog.xaml b/SpineViewer/Views/ExporterDialogs/CustomFFmpegExporterDialog.xaml
index 4f2867c..53b2b65 100644
--- a/SpineViewer/Views/ExporterDialogs/CustomFFmpegExporterDialog.xaml
+++ b/SpineViewer/Views/ExporterDialogs/CustomFFmpegExporterDialog.xaml
@@ -31,7 +31,7 @@
-
diff --git a/SpineViewer/Views/ExporterDialogs/CustomFFmpegExporterDialog.xaml.cs b/SpineViewer/Views/ExporterDialogs/CustomFFmpegExporterDialog.xaml.cs
index eda8ae4..efce9e0 100644
--- a/SpineViewer/Views/ExporterDialogs/CustomFFmpegExporterDialog.xaml.cs
+++ b/SpineViewer/Views/ExporterDialogs/CustomFFmpegExporterDialog.xaml.cs
@@ -1,4 +1,4 @@
-using SpineViewer.Natives;
+using Win32Natives;
using SpineViewer.Resources;
using SpineViewer.Services;
using SpineViewer.ViewModels.Exporters;
@@ -16,6 +16,7 @@ using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
+using SpineViewer.Extensions;
namespace SpineViewer.Views.ExporterDialogs
{
@@ -32,9 +33,8 @@ namespace SpineViewer.Views.ExporterDialogs
private void CustomFFmpegExporterDialog_SourceInitialized(object? sender, EventArgs e)
{
- var hwnd = new WindowInteropHelper(this).Handle;
- Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText);
- Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region);
+ this.SetWindowTextColor(AppResource.Color_PrimaryText);
+ this.SetWindowCaptionColor(AppResource.Color_Region);
}
private void ButtonOK_Click(object sender, RoutedEventArgs e)
diff --git a/SpineViewer/Views/ExporterDialogs/FFmpegVideoExporterDialog.xaml b/SpineViewer/Views/ExporterDialogs/FFmpegVideoExporterDialog.xaml
index 91e3efa..25fbda1 100644
--- a/SpineViewer/Views/ExporterDialogs/FFmpegVideoExporterDialog.xaml
+++ b/SpineViewer/Views/ExporterDialogs/FFmpegVideoExporterDialog.xaml
@@ -31,7 +31,7 @@
-
diff --git a/SpineViewer/Views/ExporterDialogs/FFmpegVideoExporterDialog.xaml.cs b/SpineViewer/Views/ExporterDialogs/FFmpegVideoExporterDialog.xaml.cs
index b4dbaaa..9b92f6a 100644
--- a/SpineViewer/Views/ExporterDialogs/FFmpegVideoExporterDialog.xaml.cs
+++ b/SpineViewer/Views/ExporterDialogs/FFmpegVideoExporterDialog.xaml.cs
@@ -1,4 +1,4 @@
-using SpineViewer.Natives;
+using SpineViewer.Extensions;
using SpineViewer.Resources;
using SpineViewer.Services;
using SpineViewer.ViewModels.Exporters;
@@ -16,6 +16,7 @@ using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
+using Win32Natives;
namespace SpineViewer.Views.ExporterDialogs
{
@@ -32,9 +33,8 @@ namespace SpineViewer.Views.ExporterDialogs
private void FFmpegVideoExporterDialog_SourceInitialized(object? sender, EventArgs e)
{
- var hwnd = new WindowInteropHelper(this).Handle;
- Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText);
- Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region);
+ this.SetWindowTextColor(AppResource.Color_PrimaryText);
+ this.SetWindowCaptionColor(AppResource.Color_Region);
}
private void ButtonOK_Click(object sender, RoutedEventArgs e)
diff --git a/SpineViewer/Views/ExporterDialogs/FrameExporterDialog.xaml b/SpineViewer/Views/ExporterDialogs/FrameExporterDialog.xaml
index 2da3a98..f309a2a 100644
--- a/SpineViewer/Views/ExporterDialogs/FrameExporterDialog.xaml
+++ b/SpineViewer/Views/ExporterDialogs/FrameExporterDialog.xaml
@@ -31,7 +31,7 @@
-
diff --git a/SpineViewer/Views/ExporterDialogs/FrameExporterDialog.xaml.cs b/SpineViewer/Views/ExporterDialogs/FrameExporterDialog.xaml.cs
index b34058f..e29e695 100644
--- a/SpineViewer/Views/ExporterDialogs/FrameExporterDialog.xaml.cs
+++ b/SpineViewer/Views/ExporterDialogs/FrameExporterDialog.xaml.cs
@@ -1,4 +1,4 @@
-using SpineViewer.Natives;
+using SpineViewer.Extensions;
using SpineViewer.Resources;
using SpineViewer.Services;
using SpineViewer.ViewModels.Exporters;
@@ -16,6 +16,7 @@ using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
+using Win32Natives;
namespace SpineViewer.Views.ExporterDialogs
{
@@ -32,9 +33,8 @@ namespace SpineViewer.Views.ExporterDialogs
private void FrameExporterDialog_SourceInitialized(object? sender, EventArgs e)
{
- var hwnd = new WindowInteropHelper(this).Handle;
- Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText);
- Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region);
+ this.SetWindowTextColor(AppResource.Color_PrimaryText);
+ this.SetWindowCaptionColor(AppResource.Color_Region);
}
private void ButtonOK_Click(object sender, RoutedEventArgs e)
diff --git a/SpineViewer/Views/ExporterDialogs/FrameSequenceExporterDialog.xaml b/SpineViewer/Views/ExporterDialogs/FrameSequenceExporterDialog.xaml
index 6688872..73bbb27 100644
--- a/SpineViewer/Views/ExporterDialogs/FrameSequenceExporterDialog.xaml
+++ b/SpineViewer/Views/ExporterDialogs/FrameSequenceExporterDialog.xaml
@@ -31,7 +31,7 @@
-
diff --git a/SpineViewer/Views/ExporterDialogs/FrameSequenceExporterDialog.xaml.cs b/SpineViewer/Views/ExporterDialogs/FrameSequenceExporterDialog.xaml.cs
index 1ed0c47..db4c9d4 100644
--- a/SpineViewer/Views/ExporterDialogs/FrameSequenceExporterDialog.xaml.cs
+++ b/SpineViewer/Views/ExporterDialogs/FrameSequenceExporterDialog.xaml.cs
@@ -1,4 +1,4 @@
-using SpineViewer.Natives;
+using SpineViewer.Extensions;
using SpineViewer.Resources;
using SpineViewer.Services;
using SpineViewer.ViewModels.Exporters;
@@ -16,6 +16,7 @@ using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
+using Win32Natives;
namespace SpineViewer.Views.ExporterDialogs
{
@@ -32,9 +33,8 @@ namespace SpineViewer.Views.ExporterDialogs
private void FrameSequenceExporterDialog_SourceInitialized(object? sender, EventArgs e)
{
- var hwnd = new WindowInteropHelper(this).Handle;
- Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText);
- Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region);
+ this.SetWindowTextColor(AppResource.Color_PrimaryText);
+ this.SetWindowCaptionColor(AppResource.Color_Region);
}
private void ButtonOK_Click(object sender, RoutedEventArgs e)
diff --git a/SpineViewer/Views/MainWindow.xaml b/SpineViewer/Views/MainWindow.xaml
index 06f20bf..0a2fcfa 100644
--- a/SpineViewer/Views/MainWindow.xaml
+++ b/SpineViewer/Views/MainWindow.xaml
@@ -239,7 +239,7 @@
TabStripPlacement="Bottom"
DataContext="{Binding SpineObjectTabViewModel}">
-
@@ -695,7 +695,7 @@
-
@@ -793,16 +793,6 @@
-
-
-
-
-
-
-
-
-
-
@@ -912,14 +902,16 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
@@ -1052,7 +1044,23 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SpineViewer/Views/MainWindow.xaml.cs b/SpineViewer/Views/MainWindow.xaml.cs
index f705ca1..6844fbc 100644
--- a/SpineViewer/Views/MainWindow.xaml.cs
+++ b/SpineViewer/Views/MainWindow.xaml.cs
@@ -1,8 +1,8 @@
using NLog;
using SFMLRenderer;
using Spine;
+using SpineViewer.Extensions;
using SpineViewer.Models;
-using SpineViewer.Natives;
using SpineViewer.Resources;
using SpineViewer.Services;
using SpineViewer.Utils;
@@ -24,6 +24,7 @@ using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Threading;
+using Win32Natives;
namespace SpineViewer.Views;
@@ -47,7 +48,7 @@ public partial class MainWindow : Window
private readonly List _userStateWatchers = [];
private DispatcherTimer _saveUserStateTimer;
- private readonly TimeSpan _saveTimerDelay = TimeSpan.FromSeconds(3);
+ private readonly TimeSpan _saveTimerDelay = TimeSpan.FromSeconds(1);
public bool RootGridCol0Folded
{
@@ -98,13 +99,13 @@ public partial class MainWindow : Window
// Initialize Wallpaper RenderWindow
_wallpaperRenderWindow = new(new(1, 1), "SpineViewerWallpaper", SFML.Window.Styles.None);
- _wallpaperRenderWindow.SetVisible(false);
+ _wallpaperRenderWindow.MaxFps = 30;
+
var handle = _wallpaperRenderWindow.SystemHandle;
var style = User32.GetWindowLong(handle, User32.GWL_STYLE) | User32.WS_POPUP;
- var exStyle = User32.GetWindowLong(handle, User32.GWL_EXSTYLE) | User32.WS_EX_LAYERED | User32.WS_EX_TOOLWINDOW;
+ var exStyle = User32.GetWindowLong(handle, User32.GWL_EXSTYLE) | User32.WS_EX_TOOLWINDOW;
User32.SetWindowLong(handle, User32.GWL_STYLE, style);
User32.SetWindowLong(handle, User32.GWL_EXSTYLE, exStyle);
- User32.SetLayeredWindowAttributes(handle, 0, byte.MaxValue, User32.LWA_ALPHA);
DataContext = _vm = new(_renderPanel, _wallpaperRenderWindow);
@@ -153,9 +154,8 @@ public partial class MainWindow : Window
private void MainWindow_SourceInitialized(object? sender, EventArgs e)
{
- var hwnd = new WindowInteropHelper(this).Handle;
- Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText);
- Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region);
+ this.SetWindowTextColor(AppResource.Color_PrimaryText);
+ this.SetWindowCaptionColor(AppResource.Color_Region);
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
@@ -274,7 +274,6 @@ public partial class MainWindow : Window
_vm.ExplorerListViewModel.CurrentDirectory = m.ExploringDirectory;
_vm.SFMLRendererViewModel.SetResolution(m.ResolutionX, m.ResolutionY);
- _vm.SFMLRendererViewModel.MaxFps = m.MaxFps;
_vm.SFMLRendererViewModel.Speed = m.Speed;
_vm.SFMLRendererViewModel.ShowAxis = m.ShowAxis;
_vm.SFMLRendererViewModel.BackgroundColor = m.BackgroundColor;
@@ -310,7 +309,6 @@ public partial class MainWindow : Window
ResolutionX = _vm.SFMLRendererViewModel.ResolutionX,
ResolutionY = _vm.SFMLRendererViewModel.ResolutionY,
- MaxFps = _vm.SFMLRendererViewModel.MaxFps,
Speed = _vm.SFMLRendererViewModel.Speed,
ShowAxis = _vm.SFMLRendererViewModel.ShowAxis,
BackgroundColor = _vm.SFMLRendererViewModel.BackgroundColor,
@@ -400,7 +398,6 @@ public partial class MainWindow : Window
{
case nameof(SFMLRendererViewModel.ResolutionX):
case nameof(SFMLRendererViewModel.ResolutionY):
- case nameof(SFMLRendererViewModel.MaxFps):
case nameof(SFMLRendererViewModel.Speed):
case nameof(SFMLRendererViewModel.ShowAxis):
case nameof(SFMLRendererViewModel.BackgroundColor):
@@ -440,6 +437,7 @@ public partial class MainWindow : Window
private void SFMLRendererViewModel_PropertyChanged(object? sender, PropertyChangedEventArgs e)
{
+ // XXX: 资源管理器重启后窗口会有问题无法重新显示, 需要重启应用, 否则要重新创建窗口
if (e.PropertyName == nameof(SFMLRendererViewModel.WallpaperView))
{
var wnd = _wallpaperRenderWindow;
@@ -451,17 +449,25 @@ public partial class MainWindow : Window
_logger.Error("Failed to enable wallpaper view, WorkerW not found");
return;
}
- var handle = wnd.SystemHandle;
User32.GetPrimaryScreenResolution(out var sw, out var sh);
+ _vm.SFMLRendererViewModel.SetResolution(sw, sh);
- User32.SetParent(handle, workerw);
+ var handle = wnd.SystemHandle;
+
+ // 每次都进行设置, 确保会成为顶层子窗口
+ var lastParent = User32.SetParent(handle, workerw);
+ Debug.WriteLine($"0x{lastParent:x8} = SetParent(0x{handle:x8}, 0x{workerw:x8})");
User32.SetLayeredWindowAttributes(handle, 0, byte.MaxValue, User32.LWA_ALPHA);
- _vm.SFMLRendererViewModel.SetResolution(sw, sh);
+ // XXX: 每次新设置成桌面子窗口之后, 要确保窗口 Size 发生一次变化来触发 SFML 内部的渲染视图更新
+ var ssize = new SFML.System.Vector2u(sw, sh);
+ if (lastParent != workerw && ssize == wnd.Size)
+ {
+ wnd.Size = new(sw + 1, sh);
+ }
wnd.Position = new(0, 0);
- wnd.Size = new(sw + 1, sh);
- wnd.Size = new(sw, sh);
+ wnd.Size = ssize;
wnd.SetVisible(true);
}
else
@@ -694,7 +700,7 @@ public partial class MainWindow : Window
_renderPanelButtonsPopupContainer.Child = _renderPanelButtonsPanel;
_loggerBoxContainer.Child = null;
- _loggerBoxPopupContainer.Child = _loggerRichTextBox;
+ _loggerBoxPopupContainer.Child = _loggerBoxPanel;
}
private void SwitchToNormalLayout()
@@ -704,7 +710,7 @@ public partial class MainWindow : Window
HandyControl.Controls.IconElement.SetGeometry(_fullScreenButton, AppResource.Geo_ArrowsMaximize);
_loggerBoxPopupContainer.Child = null;
- _loggerBoxContainer.Child = _loggerRichTextBox;
+ _loggerBoxContainer.Child = _loggerBoxPanel;
_renderPanelButtonsPopupContainer.Child = null;
_renderPanelButtonsContainer.Child = _renderPanelButtonsPanel;
diff --git a/SpineViewer/Views/PreferenceDialog.xaml b/SpineViewer/Views/PreferenceDialog.xaml
index 9384a2c..9bb520b 100644
--- a/SpineViewer/Views/PreferenceDialog.xaml
+++ b/SpineViewer/Views/PreferenceDialog.xaml
@@ -31,7 +31,7 @@
-
@@ -195,30 +195,8 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -248,6 +226,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -257,6 +270,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/SpineViewer/Views/PreferenceDialog.xaml.cs b/SpineViewer/Views/PreferenceDialog.xaml.cs
index 849f47b..27f7aff 100644
--- a/SpineViewer/Views/PreferenceDialog.xaml.cs
+++ b/SpineViewer/Views/PreferenceDialog.xaml.cs
@@ -1,4 +1,4 @@
-using SpineViewer.Natives;
+using SpineViewer.Extensions;
using SpineViewer.Resources;
using SpineViewer.Services;
using SpineViewer.ViewModels.Exporters;
@@ -16,6 +16,7 @@ using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
+using Win32Natives;
namespace SpineViewer.Views
{
@@ -32,9 +33,8 @@ namespace SpineViewer.Views
private void PreferenceDialog_SourceInitialized(object? sender, EventArgs e)
{
- var hwnd = new WindowInteropHelper(this).Handle;
- Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText);
- Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region);
+ this.SetWindowTextColor(AppResource.Color_PrimaryText);
+ this.SetWindowCaptionColor(AppResource.Color_Region);
}
private void ButtonOK_Click(object sender, RoutedEventArgs e)
diff --git a/SpineViewer/Views/ProgressDialog.xaml.cs b/SpineViewer/Views/ProgressDialog.xaml.cs
index fde5c03..d59f955 100644
--- a/SpineViewer/Views/ProgressDialog.xaml.cs
+++ b/SpineViewer/Views/ProgressDialog.xaml.cs
@@ -1,4 +1,4 @@
-using SpineViewer.Natives;
+using SpineViewer.Extensions;
using SpineViewer.Resources;
using SpineViewer.ViewModels;
using System;
@@ -16,6 +16,7 @@ using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
+using Win32Natives;
namespace SpineViewer.Views
{
@@ -33,9 +34,8 @@ namespace SpineViewer.Views
private void ProgressDialog_SourceInitialized(object? sender, EventArgs e)
{
- var hwnd = new WindowInteropHelper(this).Handle;
- Dwmapi.SetWindowTextColor(hwnd, AppResource.Color_PrimaryText);
- Dwmapi.SetWindowCaptionColor(hwnd, AppResource.Color_Region);
+ this.SetWindowTextColor(AppResource.Color_PrimaryText);
+ this.SetWindowCaptionColor(AppResource.Color_Region);
}
private void ProgressWindow_Loaded(object sender, RoutedEventArgs e)
diff --git a/SpineViewerCLI/SpineViewerCLI.cs b/SpineViewerCLI/SpineViewerCLI.cs
index 9bcbea3..36c2110 100644
--- a/SpineViewerCLI/SpineViewerCLI.cs
+++ b/SpineViewerCLI/SpineViewerCLI.cs
@@ -42,7 +42,7 @@ namespace SpineViewerCLI
}
catch (Exception ex)
{
- _logger.Trace(ex.ToString());
+ _logger.Debug(ex.ToString());
_logger.Fatal("Failed to execute, {0}", ex.Message);
return -1;
}
diff --git a/SpineViewerCLI/SpineViewerCLI.csproj b/SpineViewerCLI/SpineViewerCLI.csproj
index 680952a..837c393 100644
--- a/SpineViewerCLI/SpineViewerCLI.csproj
+++ b/SpineViewerCLI/SpineViewerCLI.csproj
@@ -8,7 +8,7 @@
net8.0
$(SolutionDir)out
false
- 0.16.11
+ 0.16.12
Exe
diff --git a/SpineViewer/Natives/Dwmapi.cs b/Win32Natives/Dwmapi.cs
similarity index 81%
rename from SpineViewer/Natives/Dwmapi.cs
rename to Win32Natives/Dwmapi.cs
index 29fe400..2776186 100644
--- a/SpineViewer/Natives/Dwmapi.cs
+++ b/Win32Natives/Dwmapi.cs
@@ -4,9 +4,8 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
-using System.Windows.Media;
-namespace SpineViewer.Natives
+namespace Win32Natives
{
///
/// dwmapi.dll 包装类
@@ -24,15 +23,15 @@ namespace SpineViewer.Natives
[DllImport("dwmapi.dll")]
private static extern int DwmSetWindowAttribute(IntPtr hwnd, uint dwAttribute, ref uint pvAttribute, int cbAttribute);
- public static bool SetWindowCaptionColor(IntPtr hwnd, Color color)
+ public static bool SetWindowCaptionColor(IntPtr hwnd, byte r, byte g, byte b)
{
- int c = color.R | (color.G << 8) | (color.B << 16);
+ int c = r | (g << 8) | (b << 16);
return 0 == DwmSetWindowAttribute(hwnd, DWMWA_CAPTION_COLOR, ref c, sizeof(uint));
}
- public static bool SetWindowTextColor(IntPtr hwnd, Color color)
+ public static bool SetWindowTextColor(IntPtr hwnd, byte r, byte g, byte b)
{
- int c = color.R | (color.G << 8) | (color.B << 16);
+ int c = r | (g << 8) | (b << 16);
return 0 == DwmSetWindowAttribute(hwnd, DWMWA_TEXT_COLOR, ref c, sizeof(uint));
}
diff --git a/SpineViewer/Natives/Gdi32.cs b/Win32Natives/Gdi32.cs
similarity index 96%
rename from SpineViewer/Natives/Gdi32.cs
rename to Win32Natives/Gdi32.cs
index 5845cf9..fb3e77e 100644
--- a/SpineViewer/Natives/Gdi32.cs
+++ b/Win32Natives/Gdi32.cs
@@ -7,7 +7,7 @@ using System.Text;
using System.Threading.Tasks;
using System.Windows;
-namespace SpineViewer.Natives
+namespace Win32Natives
{
///
/// gdi32.dll 包装类
diff --git a/SpineViewer/Natives/Shell32.cs b/Win32Natives/Shell32.cs
similarity index 96%
rename from SpineViewer/Natives/Shell32.cs
rename to Win32Natives/Shell32.cs
index 1a08aa6..e6ac8ba 100644
--- a/SpineViewer/Natives/Shell32.cs
+++ b/Win32Natives/Shell32.cs
@@ -7,7 +7,7 @@ using System.Text;
using System.Threading.Tasks;
using System.Windows;
-namespace SpineViewer.Natives
+namespace Win32Natives
{
///
/// shell32.dll 包装类
diff --git a/SpineViewer/Natives/User32.cs b/Win32Natives/User32.cs
similarity index 99%
rename from SpineViewer/Natives/User32.cs
rename to Win32Natives/User32.cs
index 4702bd6..0d25a62 100644
--- a/SpineViewer/Natives/User32.cs
+++ b/Win32Natives/User32.cs
@@ -7,7 +7,7 @@ using System.Text;
using System.Threading.Tasks;
using System.Windows;
-namespace SpineViewer.Natives
+namespace Win32Natives
{
///
/// user32.dll 包装类
@@ -316,7 +316,7 @@ namespace SpineViewer.Natives
workerw = FindWindowEx(progman, IntPtr.Zero, "WorkerW", null);
}
- Debug.WriteLine($"HWND(WorkerW): {workerw:x8}");
+ Debug.WriteLine($"HWND(WorkerW): 0x{workerw:x8}");
return workerw;
}
diff --git a/Win32Natives/Win32Natives.csproj b/Win32Natives/Win32Natives.csproj
new file mode 100644
index 0000000..1ae1ffe
--- /dev/null
+++ b/Win32Natives/Win32Natives.csproj
@@ -0,0 +1,14 @@
+
+
+
+ enable
+ enable
+ x64
+ x64
+ net8.0-windows
+ $(SolutionDir)out
+ false
+ 0.16.0
+
+
+