diff --git a/Ink Canvas/Helpers/InkRecognitionManager.cs b/Ink Canvas/Helpers/InkRecognitionManager.cs new file mode 100644 index 00000000..98f9cdf8 --- /dev/null +++ b/Ink Canvas/Helpers/InkRecognitionManager.cs @@ -0,0 +1,174 @@ +using System; +using System.Threading.Tasks; +using System.Windows.Ink; + +namespace Ink_Canvas.Helpers +{ + public sealed class InkRecognitionManager + { + private static InkRecognitionManager _instance; + private static readonly object _lock = new object(); + + private ModernInkProcessor _modernProcessor; + private ModernInkAnalyzer _modernAnalyzer; + private bool _isModernSystemAvailable; + private bool _isInitialized; + + public static InkRecognitionManager Instance + { + get + { + if (_instance == null) + { + lock (_lock) + { + if (_instance == null) + _instance = new InkRecognitionManager(); + } + } + + return _instance; + } + } + + private InkRecognitionManager() + { + Initialize(); + } + + private void Initialize() + { + try + { + _isModernSystemAvailable = Environment.Is64BitProcess; + if (_isModernSystemAvailable) + { + try + { + _modernProcessor = new ModernInkProcessor(); + _modernAnalyzer = new ModernInkAnalyzer(); + LogHelper.WriteLogToFile("墨迹识别管理器:使用64位现代化墨迹识别系统"); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile("WinRT API不可用,回退到IACore: " + ex.Message, LogHelper.LogType.Warning); + _isModernSystemAvailable = false; + _modernProcessor = null; + _modernAnalyzer = null; + } + } + + if (!_isModernSystemAvailable) + LogHelper.WriteLogToFile("墨迹识别管理器:使用IACore墨迹识别系统"); + + _isInitialized = true; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile("墨迹识别管理器初始化失败: " + ex.Message, LogHelper.LogType.Error); + _isInitialized = false; + } + } + + public Task RecognizeShapeAsync( + StrokeCollection strokes, + ShapeRecognitionEngineMode mode) + { + if (!_isInitialized || strokes == null || strokes.Count == 0) + return Task.FromResult(InkShapeRecognitionResult.Empty); + + try + { + if (ShapeRecognitionRouter.ResolveUseWinRt(mode) + && _isModernSystemAvailable + && _modernProcessor != null) + { + return _modernProcessor.RecognizeShapeAsync(strokes); + } + + var legacy = InkRecognizeHelper.RecognizeShapeIACore(strokes); + return Task.FromResult(InkRecognizeHelper.FromIACoreOrEmpty(legacy)); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile("墨迹形状识别失败: " + ex.Message, LogHelper.LogType.Error); + return Task.FromResult(InkShapeRecognitionResult.Empty); + } + } + + public Task CorrectInkAsync( + StrokeCollection strokes, + ShapeRecognitionEngineMode mode) + { + if (!_isInitialized || strokes == null || strokes.Count == 0) + return Task.FromResult(strokes); + + try + { + if (ShapeRecognitionRouter.ResolveUseWinRt(mode) && _modernAnalyzer != null) + return _modernAnalyzer.AnalyzeAndCorrectAsync(strokes); + + return Task.FromResult(strokes); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile("墨迹纠正失败: " + ex.Message, LogHelper.LogType.Error); + return Task.FromResult(strokes); + } + } + + public bool IsValidShapeType(string shapeName) + { + return !string.IsNullOrEmpty(shapeName) + && (shapeName.Contains("Triangle") || shapeName.Contains("Circle") + || shapeName.Contains("Rectangle") || shapeName.Contains("Diamond") + || shapeName.Contains("Parallelogram") || shapeName.Contains("Square") + || shapeName.Contains("Ellipse") || shapeName.Contains("Line") + || shapeName.Contains("Arrow")); + } + + public string GetSystemInfo() + { + return _isModernSystemAvailable + ? $"现代化64位墨迹识别系统 (Windows Runtime API) - 进程架构: {Environment.Is64BitProcess}" + : $"传统墨迹识别系统 (IACore) - 进程架构: {Environment.Is64BitProcess}"; + } + + public void Dispose() + { + _modernProcessor?.Dispose(); + _modernAnalyzer?.Dispose(); + _isInitialized = false; + } + } + + internal sealed class ModernInkProcessor : IDisposable + { + public ModernInkProcessor() + { + if (!WinRtInkShapeRecognizer.IsApiAvailable) + throw new InvalidOperationException("WinRT 墨迹分析需要 Windows 10 及以上。"); + } + + public Task RecognizeShapeAsync(StrokeCollection strokes) + { + return WinRtInkShapeRecognizer.RecognizeShapeAsync(strokes); + } + + public void Dispose() + { + } + } + + internal sealed class ModernInkAnalyzer : IDisposable + { + public Task AnalyzeAndCorrectAsync(StrokeCollection strokes) + { + return Task.FromResult(strokes); + } + + public void Dispose() + { + } + } +} diff --git a/Ink Canvas/Helpers/InkRecognizeHelper.cs b/Ink Canvas/Helpers/InkRecognizeHelper.cs index c9865604..a9f4614e 100644 --- a/Ink Canvas/Helpers/InkRecognizeHelper.cs +++ b/Ink Canvas/Helpers/InkRecognizeHelper.cs @@ -1,4 +1,5 @@ using System.Linq; +using System.Threading.Tasks; using System.Windows; using System.Windows.Ink; using System.Windows.Media; @@ -56,7 +57,7 @@ namespace Ink_Canvas.Helpers public static ShapeRecognizeResult RecognizeShape(StrokeCollection strokes) => RecognizeShapeIACore(strokes); - /// 按设置选择 WinRT 或 IACore,返回统一识别结果。 + /// 按设置选择 WinRT()或 IACore;WinRT 请用 public static InkShapeRecognitionResult RecognizeShapeUnified( StrokeCollection strokes, ShapeRecognitionEngineMode mode) @@ -65,16 +66,28 @@ namespace Ink_Canvas.Helpers return InkShapeRecognitionResult.Empty; if (ShapeRecognitionRouter.ResolveUseWinRt(mode)) - return WinRtInkShapeRecognizer.RecognizeShape(strokes); + return InkShapeRecognitionResult.Empty; var legacy = RecognizeShapeIACore(strokes); return FromIACoreOrEmpty(legacy); } + /// 与 CE 反编译版 InkRecognitionManager.RecognizeShapeAsync 对齐的统一入口。 + public static Task RecognizeShapeUnifiedAsync( + StrokeCollection strokes, + ShapeRecognitionEngineMode mode) + { + if (strokes == null || strokes.Count == 0) + return Task.FromResult(InkShapeRecognitionResult.Empty); + + return InkRecognitionManager.Instance.RecognizeShapeAsync(strokes, mode); + } + public static void WarmupShapeRecognition(ShapeRecognitionEngineMode mode) { try { + _ = InkRecognitionManager.Instance; if (ShapeRecognitionRouter.ResolveUseWinRt(mode)) WinRtInkShapeRecognizer.Warmup(); else diff --git a/Ink Canvas/Helpers/WinRtInkShapeRecognizer.cs b/Ink Canvas/Helpers/WinRtInkShapeRecognizer.cs index fc73d6dc..5662779e 100644 --- a/Ink Canvas/Helpers/WinRtInkShapeRecognizer.cs +++ b/Ink Canvas/Helpers/WinRtInkShapeRecognizer.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Runtime.InteropServices.WindowsRuntime; using System.Threading.Tasks; +using System.Windows; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; @@ -22,7 +23,19 @@ namespace Ink_Canvas.Helpers if (!IsApiAvailable) return; try { - RecognizeShape(new StrokeCollection()); + var d = Application.Current?.Dispatcher; + if (d == null) return; + d.BeginInvoke(new Action(async () => + { + try + { + await RecognizeShapeAsync(new StrokeCollection()); + } + catch + { + // ignore + } + })); } catch { @@ -30,23 +43,8 @@ namespace Ink_Canvas.Helpers } } - public static InkShapeRecognitionResult RecognizeShape(StrokeCollection strokes) - { - if (!IsApiAvailable || strokes == null || strokes.Count == 0) - return InkShapeRecognitionResult.Empty; - - try - { - return RecognizeShapeAsync(strokes).GetAwaiter().GetResult(); - } - catch (Exception ex) - { - System.Diagnostics.Debug.WriteLine(ex); - return InkShapeRecognitionResult.Empty; - } - } - - private static async Task RecognizeShapeAsync(StrokeCollection strokes) + /// / 在 UI 上 await(勿在收笔回调中同步阻塞)。 + internal static async Task RecognizeShapeAsync(StrokeCollection strokes) { var analyzer = new WinRtInkAnalyzer(); foreach (Stroke s in strokes) @@ -61,7 +59,7 @@ namespace Ink_Canvas.Helpers global::Windows.UI.Input.Inking.Analysis.InkAnalysisStrokeKind.Drawing); } - await analyzer.AnalyzeAsync().AsTask().ConfigureAwait(false); + await analyzer.AnalyzeAsync().AsTask().ConfigureAwait(true); var drawing = FindPrimaryDrawing(analyzer); if (drawing == null || diff --git a/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs b/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs index 9ab42788..4671089e 100644 --- a/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs +++ b/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs @@ -388,7 +388,7 @@ namespace Ink_Canvas Settings.InkToShape.IsInkToShapeEnabled, ShapeRecognitionRouter.FromSettingsInt(Settings.InkToShape.ShapeRecognitionEngine))) { - void InkToShapeProcess() + async Task InkToShapeProcessCoreAsync() { try { @@ -407,11 +407,11 @@ namespace Ink_Canvas var shapeMode = ShapeRecognitionRouter.FromSettingsInt(Settings.InkToShape.ShapeRecognitionEngine); var strokeReco = new StrokeCollection(); - var result = InkRecognizeHelper.RecognizeShapeUnified(newStrokes, shapeMode); + var result = await InkRecognizeHelper.RecognizeShapeUnifiedAsync(newStrokes, shapeMode); for (var i = newStrokes.Count - 1; i >= 0; i--) { strokeReco.Add(newStrokes[i]); - var newResult = InkRecognizeHelper.RecognizeShapeUnified(strokeReco, shapeMode); + var newResult = await InkRecognizeHelper.RecognizeShapeUnifiedAsync(strokeReco, shapeMode); if (newResult.IsSuccess && (newResult.ShapeName == "Circle" || newResult.ShapeName == "Ellipse")) { @@ -715,6 +715,28 @@ namespace Ink_Canvas catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } } + void InkToShapeProcess() + { + var engineMode = ShapeRecognitionRouter.FromSettingsInt(Settings.InkToShape.ShapeRecognitionEngine); + if (ShapeRecognitionRouter.ResolveUseWinRt(engineMode)) + { + Dispatcher.BeginInvoke(new Action(async () => + { + try + { + await InkToShapeProcessCoreAsync(); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex); + } + }), DispatcherPriority.Background); + return; + } + + InkToShapeProcessCoreAsync().GetAwaiter().GetResult(); + } + InkToShapeProcess(); }