diff --git a/Ink Canvas/Helpers/InkRecognitionManager.cs b/Ink Canvas/Helpers/InkRecognitionManager.cs index 98f9cdf8..dd42b114 100644 --- a/Ink Canvas/Helpers/InkRecognitionManager.cs +++ b/Ink Canvas/Helpers/InkRecognitionManager.cs @@ -40,14 +40,19 @@ namespace Ink_Canvas.Helpers { try { - _isModernSystemAvailable = Environment.Is64BitProcess; - if (_isModernSystemAvailable) + var tryModern = WinRtInkShapeRecognizer.IsApiAvailable + && (Environment.Is64BitProcess || Environment.Is64BitOperatingSystem); + + _isModernSystemAvailable = false; + if (tryModern) { try { _modernProcessor = new ModernInkProcessor(); _modernAnalyzer = new ModernInkAnalyzer(); - LogHelper.WriteLogToFile("墨迹识别管理器:使用64位现代化墨迹识别系统"); + _isModernSystemAvailable = true; + LogHelper.WriteLogToFile( + $"墨迹识别管理器:使用现代化墨迹识别系统 (WinRT) - 进程64位: {Environment.Is64BitProcess}, OS64位: {Environment.Is64BitOperatingSystem}"); } catch (Exception ex) { @@ -80,10 +85,9 @@ namespace Ink_Canvas.Helpers try { if (ShapeRecognitionRouter.ResolveUseWinRt(mode) - && _isModernSystemAvailable - && _modernProcessor != null) + && WinRtInkShapeRecognizer.IsApiAvailable) { - return _modernProcessor.RecognizeShapeAsync(strokes); + return RunWinRtOrLogAsync(strokes); } var legacy = InkRecognizeHelper.RecognizeShapeIACore(strokes); @@ -96,6 +100,19 @@ namespace Ink_Canvas.Helpers } } + private static async Task RunWinRtOrLogAsync(StrokeCollection strokes) + { + try + { + return await WinRtInkShapeRecognizer.RecognizeShapeAsync(strokes).ConfigureAwait(true); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile("WinRT 墨迹形状识别异常: " + ex, LogHelper.LogType.Error); + return InkShapeRecognitionResult.Empty; + } + } + public Task CorrectInkAsync( StrokeCollection strokes, ShapeRecognitionEngineMode mode) diff --git a/Ink Canvas/Helpers/InkRecognizeHelper.cs b/Ink Canvas/Helpers/InkRecognizeHelper.cs index a9f4614e..34f5cfe6 100644 --- a/Ink Canvas/Helpers/InkRecognizeHelper.cs +++ b/Ink Canvas/Helpers/InkRecognizeHelper.cs @@ -26,19 +26,29 @@ namespace Ink_Canvas.Helpers var alternates = analyzer.GetAlternates(); if (alternates.Count > 0) { - while ((!alternates[0].Strokes.Contains(strokes.Last()) || - !IsContainShapeType(((InkDrawingNode)alternates[0].AlternateNodes[0]).GetShapeName())) - && strokesCount >= 2) + while (strokesCount >= 2) { + var alt0 = alternates[0]; + if (alt0?.AlternateNodes == null || alt0.AlternateNodes.Count == 0) + break; + var drawNode = alt0.AlternateNodes[0] as InkDrawingNode; + if (drawNode == null) + break; + var shapeOk = IsContainShapeType(drawNode.GetShapeName()); + if (alt0.Strokes.Contains(strokes.Last()) && shapeOk) + break; analyzer.RemoveStroke(strokes[strokes.Count - strokesCount]); strokesCount--; sfsaf = analyzer.Analyze(); if (sfsaf.Successful) - { alternates = analyzer.GetAlternates(); - } + else + break; + if (alternates.Count == 0) + break; } - analysisAlternate = alternates[0]; + if (alternates.Count > 0) + analysisAlternate = alternates[0]; } } @@ -47,6 +57,8 @@ namespace Ink_Canvas.Helpers if (analysisAlternate != null && analysisAlternate.AlternateNodes.Count > 0) { var node = analysisAlternate.AlternateNodes[0] as InkDrawingNode; + if (node == null) + return default; return new ShapeRecognizeResult(node.Centroid, node.HotPoints, analysisAlternate, node); } diff --git a/Ink Canvas/Helpers/WinRtInkShapeRecognizer.cs b/Ink Canvas/Helpers/WinRtInkShapeRecognizer.cs index 5662779e..3ccf3263 100644 --- a/Ink Canvas/Helpers/WinRtInkShapeRecognizer.cs +++ b/Ink Canvas/Helpers/WinRtInkShapeRecognizer.cs @@ -46,41 +46,70 @@ namespace Ink_Canvas.Helpers /// / 在 UI 上 await(勿在收笔回调中同步阻塞)。 internal static async Task RecognizeShapeAsync(StrokeCollection strokes) { - var analyzer = new WinRtInkAnalyzer(); - foreach (Stroke s in strokes) + if (!IsApiAvailable || strokes == null || strokes.Count == 0) + return InkShapeRecognitionResult.Empty; + + try { - var inkStroke = CreateInkStrokeFromWpf(s); - if (inkStroke == null) - continue; + var analyzer = new WinRtInkAnalyzer(); + var added = 0; + foreach (Stroke s in strokes) + { + var inkStroke = CreateInkStrokeFromWpf(s); + if (inkStroke == null) + continue; - analyzer.AddDataForStroke(inkStroke); - analyzer.SetStrokeDataKind( - inkStroke.Id, - global::Windows.UI.Input.Inking.Analysis.InkAnalysisStrokeKind.Drawing); + analyzer.AddDataForStroke(inkStroke); + analyzer.SetStrokeDataKind( + inkStroke.Id, + global::Windows.UI.Input.Inking.Analysis.InkAnalysisStrokeKind.Drawing); + added++; + } + + if (added == 0) + { + LogHelper.WriteLogToFile( + "WinRT 形状识别:未能从 WPF 笔迹生成 InkStroke(检查 StylusPoints/DrawingAttributes)。", + LogHelper.LogType.Warning); + return InkShapeRecognitionResult.Empty; + } + + await analyzer.AnalyzeAsync().AsTask().ConfigureAwait(true); + + var drawing = FindPrimaryDrawing(analyzer); + if (drawing == null) + { + LogHelper.WriteLogToFile("WinRT 形状识别:分析完成但未找到 InkAnalysisInkDrawing 节点。", LogHelper.LogType.Trace); + return InkShapeRecognitionResult.Empty; + } + + if (drawing.DrawingKind == global::Windows.UI.Input.Inking.Analysis.InkAnalysisDrawingKind.Drawing) + { + LogHelper.WriteLogToFile("WinRT 形状识别:结果为 Drawing(未识别为规则形状)。", LogHelper.LogType.Trace); + return InkShapeRecognitionResult.Empty; + } + + var name = MapDrawingKindToShapeName(drawing.DrawingKind); + if (string.IsNullOrEmpty(name) || name == "Drawing") + return InkShapeRecognitionResult.Empty; + + var winPts = CopyWinRtPoints(drawing); + var hot = ToWpfPointCollection(winPts); + var c = drawing.Center; + var centroid = new SysPoint(c.X, c.Y); + BoundsFromPoints(winPts, out double w, out double h); + + var toRemove = new StrokeCollection(); + foreach (Stroke s in strokes) + toRemove.Add(s); + + return new InkShapeRecognitionResult(name, centroid, hot, w, h, toRemove); } - - await analyzer.AnalyzeAsync().AsTask().ConfigureAwait(true); - - var drawing = FindPrimaryDrawing(analyzer); - if (drawing == null || - drawing.DrawingKind == global::Windows.UI.Input.Inking.Analysis.InkAnalysisDrawingKind.Drawing) + catch (Exception ex) + { + LogHelper.WriteLogToFile("WinRtInkShapeRecognizer 异常: " + ex, LogHelper.LogType.Error); return InkShapeRecognitionResult.Empty; - - var name = MapDrawingKindToShapeName(drawing.DrawingKind); - if (string.IsNullOrEmpty(name) || name == "Drawing") - return InkShapeRecognitionResult.Empty; - - var winPts = CopyWinRtPoints(drawing); - var hot = ToWpfPointCollection(winPts); - var c = drawing.Center; - var centroid = new SysPoint(c.X, c.Y); - BoundsFromPoints(winPts, out double w, out double h); - - var toRemove = new StrokeCollection(); - foreach (Stroke s in strokes) - toRemove.Add(s); - - return new InkShapeRecognitionResult(name, centroid, hot, w, h, toRemove); + } } private static global::Windows.UI.Input.Inking.InkStroke CreateInkStrokeFromWpf(Stroke stroke) @@ -117,7 +146,8 @@ namespace Ink_Canvas.Helpers { global::Windows.UI.Input.Inking.Analysis.InkAnalysisInkDrawing best = null; double bestArea = -1; - Visit(analyzer.AnalysisRoot); + if (analyzer?.AnalysisRoot != null) + Visit(analyzer.AnalysisRoot); return best; void Visit(global::Windows.UI.Input.Inking.Analysis.IInkAnalysisNode node) @@ -135,7 +165,11 @@ namespace Ink_Canvas.Helpers } } - foreach (var child in node.Children) + // WinRT IInkAnalysisNode.Children 可能为 null,不可直接 foreach。 + var children = node.Children; + if (children == null) return; + + foreach (var child in children) Visit(child); } } diff --git a/Ink Canvas/InkCanvasForClass.csproj b/Ink Canvas/InkCanvasForClass.csproj index 389b5727..4a03f9d3 100644 --- a/Ink Canvas/InkCanvasForClass.csproj +++ b/Ink Canvas/InkCanvasForClass.csproj @@ -30,17 +30,17 @@ embedded bin\$(Configuration)\ - True + false embedded bin\$(Configuration)\ - True + false embedded bin\$(Configuration)\ - True + false Resources\icc.ico diff --git a/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs b/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs index 4671089e..9dceb38a 100644 --- a/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs +++ b/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; @@ -39,6 +40,9 @@ namespace Ink_Canvas /// private List circles = new List(); + /// 串行执行墨迹转形状(尤其 WinRT 异步延后),避免多笔 BeginInvoke 交错修改 newStrokes。 + private static readonly SemaphoreSlim InkToShapeSerial = new SemaphoreSlim(1, 1); + /// @@ -390,6 +394,7 @@ namespace Ink_Canvas { async Task InkToShapeProcessCoreAsync() { + await InkToShapeSerial.WaitAsync().ConfigureAwait(true); try { newStrokes.Add(e.Stroke); @@ -713,6 +718,10 @@ namespace Ink_Canvas } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } + finally + { + InkToShapeSerial.Release(); + } } void InkToShapeProcess() @@ -730,7 +739,7 @@ namespace Ink_Canvas { System.Diagnostics.Debug.WriteLine(ex); } - }), DispatcherPriority.Background); + }), DispatcherPriority.Normal); return; }